1de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonpackage com.android.exchange.adapter; 2de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 3de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.ContentProviderOperation; 4de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.ContentProviderResult; 5de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.ContentResolver; 6de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.ContentUris; 7de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.ContentValues; 8de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.Context; 9de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.content.OperationApplicationException; 10de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.database.Cursor; 11de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.net.Uri; 12de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.os.RemoteException; 13de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.os.TransactionTooLargeException; 14de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract; 15de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.Attendees; 16de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.Calendars; 17de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.Events; 18de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.ExtendedProperties; 19de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.Reminders; 20de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.provider.CalendarContract.SyncState; 21110837ebff288a75f9bda067c38e2c46797d99b5Alon Albertimport android.provider.SyncStateContract; 22de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport android.text.format.DateUtils; 23de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 24de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.emailcommon.provider.Account; 25de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.emailcommon.provider.Mailbox; 26de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.emailcommon.utility.Utility; 27de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.exchange.Eas; 28de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.exchange.adapter.AbstractSyncAdapter.Operation; 2928f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdonimport com.android.exchange.service.EasCalendarSyncHandler; 30de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.exchange.utility.CalendarUtilities; 31de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.android.mail.utils.LogUtils; 32de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport com.google.common.annotations.VisibleForTesting; 33de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 34de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport java.io.IOException; 35de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport java.io.InputStream; 36de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport java.util.ArrayList; 37de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport java.util.GregorianCalendar; 38de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonimport java.util.Map.Entry; 39110837ebff288a75f9bda067c38e2c46797d99b5Alon Albertimport java.util.TimeZone; 40de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 41de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdonpublic class CalendarSyncParser extends AbstractSyncParser { 42110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String TAG = Eas.LOG_TAG; 43de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 44de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); 45de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final TimeZone mLocalTimeZone = TimeZone.getDefault(); 46de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 47de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final long mCalendarId; 48de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final android.accounts.Account mAccountManagerAccount; 49de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterAttendees; 50de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterEvents; 51de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 52de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final String[] mBindArgument = new String[1]; 53de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final CalendarOperations mOps; 54de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 55de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 56de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EVENT_SAVED_TIMEZONE_COLUMN = Events.SYNC_DATA1; 57de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Since exceptions will have the same _SYNC_ID as the original event we have to check that 58de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // there's no original event when finding an item by _SYNC_ID 59de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String SERVER_ID_AND_CALENDAR_ID = Events._SYNC_ID + "=? AND " + 60de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Events.ORIGINAL_SYNC_ID + " ISNULL AND " + Events.CALENDAR_ID + "=?"; 61de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String CLIENT_ID_SELECTION = Events.SYNC_DATA2 + "=?"; 62de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String ATTENDEES_EXCEPT_ORGANIZER = Attendees.EVENT_ID + "=? AND " + 63de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Attendees.ATTENDEE_RELATIONSHIP + "!=" + Attendees.RELATIONSHIP_ORGANIZER; 64de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String[] ID_PROJECTION = new String[] {Events._ID}; 65de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EVENT_ID_AND_NAME = 66de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ExtendedProperties.EVENT_ID + "=? AND " + ExtendedProperties.NAME + "=?"; 67de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 68de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String[] EXTENDED_PROPERTY_PROJECTION = 69de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon new String[] {ExtendedProperties._ID}; 70de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final int EXTENDED_PROPERTY_ID = 0; 71de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 72de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String CATEGORY_TOKENIZER_DELIMITER = "\\"; 73de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String ATTENDEE_TOKENIZER_DELIMITER = CATEGORY_TOKENIZER_DELIMITER; 74de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 75de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_USER_ATTENDEE_STATUS = "userAttendeeStatus"; 76de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_ATTENDEES = "attendees"; 77de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_DTSTAMP = "dtstamp"; 78de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_MEETING_STATUS = "meeting_status"; 79de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_CATEGORIES = "categories"; 80de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Used to indicate that we removed the attendee list because it was too large 81de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_ATTENDEES_REDACTED = "attendeesRedacted"; 82de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Used to indicate that upsyncs aren't allowed (we catch this in sendLocalChanges) 83de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String EXTENDED_PROPERTY_UPSYNC_PROHIBITED = "upsyncProhibited"; 84de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 85de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final Operation PLACEHOLDER_OPERATION = 86de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon new Operation(ContentProviderOperation.newInsert(Uri.EMPTY)); 87de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 88de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final long SEPARATOR_ID = Long.MAX_VALUE; 89de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 90de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Maximum number of allowed attendees; above this number, we mark the Event with the 91de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // attendeesRedacted extended property and don't allow the event to be upsynced to the server 92de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final int MAX_SYNCED_ATTENDEES = 50; 93de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We set the organizer to this when the user is the organizer and we've redacted the 94de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // attendee list. By making the meeting organizer OTHER than the user, we cause the UI to 95de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // prevent edits to this event (except local changes like reminder). 96de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final String BOGUS_ORGANIZER_EMAIL = "upload_disallowed@uploadisdisallowed.aaa"; 97de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Maximum number of CPO's before we start redacting attendees in exceptions 98de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // The number 500 has been determined empirically; 1500 CPOs appears to be the limit before 99de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // binder failures occur, but we need room at any point for additional events/exceptions so 100de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // we set our limit at 1/3 of the apparent maximum for extra safety 101de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // TODO Find a better solution to this workaround 102de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final int MAX_OPS_BEFORE_EXCEPTION_ATTENDEE_REDACTION = 500; 103de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 104de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public CalendarSyncParser(final Context context, final ContentResolver resolver, 105de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final InputStream in, final Mailbox mailbox, final Account account, 106de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final android.accounts.Account accountManagerAccount, 107de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final long calendarId) throws IOException { 108de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon super(context, resolver, in, mailbox, account); 109de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAccountManagerAccount = accountManagerAccount; 110de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mCalendarId = calendarId; 111de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterAttendees = asSyncAdapter(Attendees.CONTENT_URI, 112de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAccount.mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE); 113de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterEvents = asSyncAdapter(Events.CONTENT_URI, 114de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAccount.mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE); 115de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mOps = new CalendarOperations(resolver, mAsSyncAdapterAttendees, mAsSyncAdapterEvents, 116de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon asSyncAdapter(Reminders.CONTENT_URI, mAccount.mEmailAddress, 117de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), 118de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon asSyncAdapter(ExtendedProperties.CONTENT_URI, mAccount.mEmailAddress, 119de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)); 120de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 121de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 122de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon protected static class CalendarOperations extends ArrayList<Operation> { 123de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static final long serialVersionUID = 1L; 124de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public int mCount = 0; 125de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private int mEventStart = 0; 126de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final ContentResolver mContentResolver; 127de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterAttendees; 128de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterEvents; 129de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterReminders; 130de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private final Uri mAsSyncAdapterExtendedProperties; 131de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 132de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public CalendarOperations(final ContentResolver contentResolver, 133de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final Uri asSyncAdapterAttendees, final Uri asSyncAdapterEvents, 134de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final Uri asSyncAdapterReminders, final Uri asSyncAdapterExtendedProperties) { 135de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mContentResolver = contentResolver; 136de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterAttendees = asSyncAdapterAttendees; 137de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterEvents = asSyncAdapterEvents; 138de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterReminders = asSyncAdapterReminders; 139de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAsSyncAdapterExtendedProperties = asSyncAdapterExtendedProperties; 140de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 141de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 142de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon @Override 143de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public boolean add(Operation op) { 144de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon super.add(op); 145de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mCount++; 146de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return true; 147de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 148de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 149de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public int newEvent(Operation op) { 150de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mEventStart = mCount; 151de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(op); 152de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return mEventStart; 153de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 154de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 155de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public int newDelete(long id, String serverId) { 156de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int offset = mCount; 157de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon delete(id, serverId); 158de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return offset; 159de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 160de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 161de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newAttendee(ContentValues cv) { 162de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon newAttendee(cv, mEventStart); 163de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 164de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 165de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newAttendee(ContentValues cv, int eventStart) { 166de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterAttendees) 167de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValues(cv), 168de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Attendees.EVENT_ID, 169de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventStart)); 170de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 171de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 172de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void updatedAttendee(ContentValues cv, long id) { 173de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.EVENT_ID, id); 174de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterAttendees) 175de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValues(cv))); 176de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 177de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 178de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newException(ContentValues cv) { 179de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterEvents) 180de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValues(cv))); 181de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 182de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 183de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newExtendedProperty(String name, String value) { 184de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterExtendedProperties) 185de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValue(ExtendedProperties.NAME, name) 186de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValue(ExtendedProperties.VALUE, value), 187de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ExtendedProperties.EVENT_ID, 188de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mEventStart)); 189de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 190de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 191de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void updatedExtendedProperty(String name, String value, long id) { 192de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Find an existing ExtendedProperties row for this event and property name 193de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Cursor c = mContentResolver.query(ExtendedProperties.CONTENT_URI, 194de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon EXTENDED_PROPERTY_PROJECTION, EVENT_ID_AND_NAME, 195de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon new String[] {Long.toString(id), name}, null); 196de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long extendedPropertyId = -1; 197de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If there is one, capture its _id 198de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c != null) { 199de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 200de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c.moveToFirst()) { 201de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon extendedPropertyId = c.getLong(EXTENDED_PROPERTY_ID); 202de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 203de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } finally { 204de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon c.close(); 205de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 206de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 207de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Either do an update or an insert, depending on whether one 208de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // already exists 209de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (extendedPropertyId >= 0) { 210de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation 211de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .newUpdate( 212de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentUris.withAppendedId(mAsSyncAdapterExtendedProperties, 213de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon extendedPropertyId)) 214de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValue(ExtendedProperties.VALUE, value))); 215de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 216de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon newExtendedProperty(name, value); 217de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 218de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 219de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 220de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newReminder(int mins, int eventStart) { 221de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterReminders) 222de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValue(Reminders.MINUTES, mins) 223de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValue(Reminders.METHOD, Reminders.METHOD_ALERT), 224de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ExtendedProperties.EVENT_ID, 225de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventStart)); 226de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 227de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 228de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void newReminder(int mins) { 229de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon newReminder(mins, mEventStart); 230de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 231de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 232de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void delete(long id, String syncId) { 233de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation.newDelete( 234de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentUris.withAppendedId(mAsSyncAdapterEvents, id)))); 235de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Delete the exceptions for this Event (CalendarProvider doesn't do this) 236de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon add(new Operation(ContentProviderOperation 237de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .newDelete(mAsSyncAdapterEvents) 238de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withSelection(Events.ORIGINAL_SYNC_ID + "=?", new String[] {syncId}))); 239de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 240de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 241de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 242de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static Uri asSyncAdapter(Uri uri, String account, String accountType) { 243de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") 244de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .appendQueryParameter(Calendars.ACCOUNT_NAME, account) 245de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); 246de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 247de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 248de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static void addOrganizerToAttendees(CalendarOperations ops, long eventId, 249de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String organizerName, String organizerEmail) { 250de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Handle the organizer (who IS an attendee on device, but NOT in EAS) 251de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (organizerName != null || organizerEmail != null) { 252de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues attendeeCv = new ContentValues(); 253de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (organizerName != null) { 254de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCv.put(Attendees.ATTENDEE_NAME, organizerName); 255de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 256de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (organizerEmail != null) { 257de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCv.put(Attendees.ATTENDEE_EMAIL, organizerEmail); 258de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 259de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCv.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER); 260de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCv.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED); 261de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCv.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED); 262de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventId < 0) { 263de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newAttendee(attendeeCv); 264de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 265de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedAttendee(attendeeCv, eventId); 266de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 267de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 268de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 269de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 270de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 271de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Set DTSTART, DTEND, DURATION and EVENT_TIMEZONE as appropriate for the given Event 272de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * The follow rules are enforced by CalendarProvider2: 273de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Events that aren't exceptions MUST have either 1) a DTEND or 2) a DURATION 274de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Recurring events (i.e. events with RRULE) must have a DURATION 275de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * All-day recurring events MUST have a DURATION that is in the form P<n>D 276de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Other events MAY have a DURATION in any valid form (we use P<n>M) 277de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * All-day events MUST have hour, minute, and second = 0; in addition, they must have 278de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * the EVENT_TIMEZONE set to UTC 279de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Also, exceptions to all-day events need to have an ORIGINAL_INSTANCE_TIME that has 280de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * hour, minute, and second = 0 and be set in UTC 281de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @param cv the ContentValues for the Event 282de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @param startTime the start time for the Event 283de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @param endTime the end time for the Event 284de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @param allDayEvent whether this is an all day event (1) or not (0) 285de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 286de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /*package*/ void setTimeRelatedValues(ContentValues cv, long startTime, long endTime, 287de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int allDayEvent) { 288de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If there's no startTime, the event will be found to be invalid, so return 289de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (startTime < 0) return; 290de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // EAS events can arrive without an end time, but CalendarProvider requires them 291de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // so we'll default to 30 minutes; this will be superceded if this is an all-day event 292de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (endTime < 0) endTime = startTime + (30 * DateUtils.MINUTE_IN_MILLIS); 293de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 294de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If this is an all-day event, set hour, minute, and second to zero, and use UTC 295de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (allDayEvent != 0) { 296de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon startTime = CalendarUtilities.getUtcAllDayCalendarTime(startTime, mLocalTimeZone); 297de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon endTime = CalendarUtilities.getUtcAllDayCalendarTime(endTime, mLocalTimeZone); 298de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String originalTimeZone = cv.getAsString(Events.EVENT_TIMEZONE); 299de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(EVENT_SAVED_TIMEZONE_COLUMN, originalTimeZone); 300de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_TIMEZONE, UTC_TIMEZONE.getID()); 301de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 302de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 303de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If this is an exception, and the original was an all-day event, make sure the 304de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // original instance time has hour, minute, and second set to zero, and is in UTC 305de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (cv.containsKey(Events.ORIGINAL_INSTANCE_TIME) && 306de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.containsKey(Events.ORIGINAL_ALL_DAY)) { 307de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Integer ade = cv.getAsInteger(Events.ORIGINAL_ALL_DAY); 308de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (ade != null && ade != 0) { 309de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long exceptionTime = cv.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 310de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final GregorianCalendar cal = new GregorianCalendar(UTC_TIMEZONE); 311de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon exceptionTime = CalendarUtilities.getUtcAllDayCalendarTime(exceptionTime, 312de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mLocalTimeZone); 313de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.setTimeInMillis(exceptionTime); 314de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.set(GregorianCalendar.HOUR_OF_DAY, 0); 315de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.set(GregorianCalendar.MINUTE, 0); 316de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.set(GregorianCalendar.SECOND, 0); 317de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORIGINAL_INSTANCE_TIME, cal.getTimeInMillis()); 318de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 319de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 320de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 321de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Always set DTSTART 322de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DTSTART, startTime); 323de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For recurring events, set DURATION. Use P<n>D format for all day events 324de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (cv.containsKey(Events.RRULE)) { 325de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (allDayEvent != 0) { 326de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DURATION, "P" + ((endTime - startTime) / DateUtils.DAY_IN_MILLIS) + "D"); 327de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 328de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon else { 329de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DURATION, "P" + ((endTime - startTime) / DateUtils.MINUTE_IN_MILLIS) + "M"); 330de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 331de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For other events, set DTEND and LAST_DATE 332de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 333de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DTEND, endTime); 334de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.LAST_DATE, endTime); 335de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 336de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 337de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 338de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void addEvent(CalendarOperations ops, String serverId, boolean update) 339de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throws IOException { 340de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues cv = new ContentValues(); 341de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.CALENDAR_ID, mCalendarId); 342de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events._SYNC_ID, serverId); 343de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.HAS_ATTENDEE_DATA, 1); 344de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.SYNC_DATA2, "0"); 345de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 346de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int allDayEvent = 0; 347de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String organizerName = null; 348de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String organizerEmail = null; 349de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int eventOffset = -1; 350de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int deleteOffset = -1; 351de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int busyStatus = CalendarUtilities.BUSY_STATUS_TENTATIVE; 352de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int responseType = CalendarUtilities.RESPONSE_TYPE_NONE; 353de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 354de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean firstTag = true; 355de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long eventId = -1; 356de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long startTime = -1; 357de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long endTime = -1; 358de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon TimeZone timeZone = null; 359de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 360de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Keep track of the attendees; exceptions will need them 361de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<ContentValues> attendeeValues = new ArrayList<ContentValues>(); 362de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int reminderMins = -1; 363de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String dtStamp = null; 364de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean organizerAdded = false; 365de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 366de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 367de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (update && firstTag) { 368de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Find the event that's being updated 369de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Cursor c = getServerIdCursor(serverId); 370de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long id = -1; 371de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 372de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c != null && c.moveToFirst()) { 373de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon id = c.getLong(0); 374de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 375de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } finally { 376de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c != null) c.close(); 377de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 378de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (id > 0) { 379de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // DTSTAMP can come first, and we simply need to track it 380de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (tag == Tags.CALENDAR_DTSTAMP) { 381de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon dtStamp = getValue(); 382de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon continue; 383de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (tag == Tags.CALENDAR_ATTENDEES) { 384de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // This is an attendees-only update; just 385de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // delete/re-add attendees 386de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mBindArgument[0] = Long.toString(id); 387de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.add(new Operation(ContentProviderOperation 388de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .newDelete(mAsSyncAdapterAttendees) 389de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withSelection(ATTENDEES_EXCEPT_ORGANIZER, mBindArgument))); 390de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventId = id; 391de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 392de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Otherwise, delete the original event and recreate it 393de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Changing (delete/add) event ", serverId); 394de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon deleteOffset = ops.newDelete(id, serverId); 395de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Add a placeholder event so that associated tables can reference 396de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // this as a back reference. We add the event at the end of the method 397de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventOffset = ops.newEvent(PLACEHOLDER_OPERATION); 398de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 399de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 400de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // The changed item isn't found. We'll treat this as a new item 401de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventOffset = ops.newEvent(PLACEHOLDER_OPERATION); 402de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog(TAG, "Changed item not found; treating as new."); 403de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 404de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (firstTag) { 405de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Add a placeholder event so that associated tables can reference 406de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // this as a back reference. We add the event at the end of the method 407de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventOffset = ops.newEvent(PLACEHOLDER_OPERATION); 408de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 409de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon firstTag = false; 410de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 411de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ALL_DAY_EVENT: 412de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon allDayEvent = getValueInt(); 413de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (allDayEvent != 0 && timeZone != null) { 414de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If the event doesn't start at midnight local time, we won't consider 415de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // this an all-day event in the local time zone (this is what OWA does) 416de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon GregorianCalendar cal = new GregorianCalendar(mLocalTimeZone); 417de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.setTimeInMillis(startTime); 418de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("All-day event arrived in: " + timeZone.getID()); 419de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (cal.get(GregorianCalendar.HOUR_OF_DAY) != 0 || 420de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cal.get(GregorianCalendar.MINUTE) != 0) { 421de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon allDayEvent = 0; 422de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Not an all-day event locally: " + mLocalTimeZone.getID()); 423de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 424de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 425de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ALL_DAY, allDayEvent); 426de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 427de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTACHMENTS: 428de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attachmentsParser(); 429de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 430de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEES: 431de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If eventId >= 0, this is an update; otherwise, a new Event 432de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeValues = attendeesParser(); 433de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 434de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.BASE_BODY: 435de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DESCRIPTION, bodyParser()); 436de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 437de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_BODY: 438de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DESCRIPTION, getValue()); 439de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 440de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_TIME_ZONE: 441de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon timeZone = CalendarUtilities.tziStringToTimeZone(getValue()); 442de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (timeZone == null) { 443de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon timeZone = mLocalTimeZone; 444de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 445de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_TIMEZONE, timeZone.getID()); 446de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 447de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_START_TIME: 448de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon startTime = Utility.parseDateTimeToMillis(getValue()); 449de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 450de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_END_TIME: 451de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon endTime = Utility.parseDateTimeToMillis(getValue()); 452de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 453de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_EXCEPTIONS: 454de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For exceptions to show the organizer, the organizer must be added before 455de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // we call exceptionsParser 456de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addOrganizerToAttendees(ops, eventId, organizerName, organizerEmail); 457de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon organizerAdded = true; 458de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon exceptionsParser(ops, cv, attendeeValues, reminderMins, busyStatus, 459de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon startTime, endTime); 460de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 461de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_LOCATION: 462de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_LOCATION, getValue()); 463de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 464de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE: 465de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String rrule = recurrenceParser(); 466de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (rrule != null) { 467de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.RRULE, rrule); 468de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 469de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 470de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ORGANIZER_EMAIL: 471de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon organizerEmail = getValue(); 472de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORGANIZER, organizerEmail); 473de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 474de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_SUBJECT: 475de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.TITLE, getValue()); 476de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 477de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_SENSITIVITY: 478de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ACCESS_LEVEL, encodeVisibility(getValueInt())); 479de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 480de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ORGANIZER_NAME: 481de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon organizerName = getValue(); 482de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 483de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_REMINDER_MINS_BEFORE: 484de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Save away whether this tag has content; Exchange 2010 sends an empty tag 485de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // rather than not sending one (as with Ex07 and Ex03) 486de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean hasContent = !noContent; 487de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon reminderMins = getValueInt(); 488de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (hasContent) { 489de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newReminder(reminderMins); 490de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.HAS_ALARM, 1); 491de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 492de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 493de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // The following are fields we should save (for changes), though they don't 494de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // relate to data used by CalendarProvider at this point 495de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_UID: 496de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.SYNC_DATA2, getValue()); 497de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 498de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_DTSTAMP: 499de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon dtStamp = getValue(); 500de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 501de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_MEETING_STATUS: 502de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_MEETING_STATUS, getValue()); 503de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 504de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_BUSY_STATUS: 505de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We'll set the user's status in the Attendees table below 506de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Don't set selfAttendeeStatus or CalendarProvider will create a duplicate 507de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // attendee! 508de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon busyStatus = getValueInt(); 509de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 510de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RESPONSE_TYPE: 511de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // EAS 14+ uses this for the user's response status; we'll use this instead 512de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // of busy status, if it appears 513de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon responseType = getValueInt(); 514de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 515de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_CATEGORIES: 516de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String categories = categoriesParser(); 517de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (categories.length() > 0) { 518de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_CATEGORIES, categories); 519de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 520de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 521de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 522de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 523de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 524de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 525de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 526de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Enforce CalendarProvider required properties 527de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon setTimeRelatedValues(cv, startTime, endTime, allDayEvent); 528de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 529de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Set user's availability 530de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.AVAILABILITY, CalendarUtilities.availabilityFromBusyStatus(busyStatus)); 531de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 532de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If we haven't added the organizer to attendees, do it now 533de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (!organizerAdded) { 534de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addOrganizerToAttendees(ops, eventId, organizerName, organizerEmail); 535de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 536de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 537de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Note that organizerEmail can be null with a DTSTAMP only change from the server 538de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean selfOrganizer = (mAccount.mEmailAddress.equals(organizerEmail)); 539de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 540de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Store email addresses of attendees (in a tokenizable string) in ExtendedProperties 541de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If the user is an attendee, set the attendee status using busyStatus (note that the 542de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // busyStatus is inherited from the parent unless it's specified in the exception) 543de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Add the insert/update operation for each attendee (based on whether it's add/change) 544de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int numAttendees = attendeeValues.size(); 545de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (numAttendees > MAX_SYNCED_ATTENDEES) { 546de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Indicate that we've redacted attendees. If we're the organizer, disable edit 547de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // by setting organizerEmail to a bogus value and by setting the upsync prohibited 548de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // extended properly. 549de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Note that we don't set ANY attendees if we're in this branch; however, the 550de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // organizer has already been included above, and WILL show up (which is good) 551de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventId < 0) { 552de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "1"); 553de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (selfOrganizer) { 554de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "1"); 555de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 556de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 557de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "1", eventId); 558de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (selfOrganizer) { 559de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "1", 560de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventId); 561de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 562de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 563de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (selfOrganizer) { 564de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon organizerEmail = BOGUS_ORGANIZER_EMAIL; 565de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORGANIZER, organizerEmail); 566de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 567de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Tell UI that we don't have any attendees 568de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.HAS_ATTENDEE_DATA, "0"); 5693eef378426c7c88608f53f5a268baed40259ccf6Alon Albert LogUtils.d(TAG, "Maximum number of attendees exceeded; redacting"); 570de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (numAttendees > 0) { 571de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon StringBuilder sb = new StringBuilder(); 572de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (ContentValues attendee: attendeeValues) { 573de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String attendeeEmail = attendee.getAsString(Attendees.ATTENDEE_EMAIL); 574de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon sb.append(attendeeEmail); 575de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon sb.append(ATTENDEE_TOKENIZER_DELIMITER); 576de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (mAccount.mEmailAddress.equalsIgnoreCase(attendeeEmail)) { 577de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int attendeeStatus; 578de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We'll use the response type (EAS 14), if we've got one; otherwise, we'll 579de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // try to infer it from busy status 580de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (responseType != CalendarUtilities.RESPONSE_TYPE_NONE) { 581de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeStatus = 582de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon CalendarUtilities.attendeeStatusFromResponseType(responseType); 583de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (!update) { 584de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For new events in EAS < 14, we have no idea what the busy status 585de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // means, so we show "none", allowing the user to select an option. 586de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeStatus = Attendees.ATTENDEE_STATUS_NONE; 587de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 588de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // For updated events, we'll try to infer the attendee status from the 589de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // busy status 590de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeStatus = 591de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus); 592de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 593de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendee.put(Attendees.ATTENDEE_STATUS, attendeeStatus); 594de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If we're an attendee, save away our initial attendee status in the 595de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // event's ExtendedProperties (we look for differences between this and 596de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // the user's current attendee status to determine whether an email needs 597de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // to be sent to the organizer) 598de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // organizerEmail will be null in the case that this is an attendees-only 599de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // change from the server 600de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (organizerEmail == null || 601de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon !organizerEmail.equalsIgnoreCase(attendeeEmail)) { 602de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventId < 0) { 603de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_USER_ATTENDEE_STATUS, 604de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Integer.toString(attendeeStatus)); 605de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 606de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_USER_ATTENDEE_STATUS, 607de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Integer.toString(attendeeStatus), eventId); 608de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 609de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 610de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 611de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 612de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventId < 0) { 613de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newAttendee(attendee); 614de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 615de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedAttendee(attendee, eventId); 616de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 617de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 618de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventId < 0) { 619de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES, sb.toString()); 620de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "0"); 621de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "0"); 622de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 623de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES, sb.toString(), 624de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon eventId); 625de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "0", eventId); 626de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.updatedExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "0", eventId); 627de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 628de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 629de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 630de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Put the real event in the proper place in the ops ArrayList 631de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (eventOffset >= 0) { 632de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Store away the DTSTAMP here 633de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (dtStamp != null) { 634de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newExtendedProperty(EXTENDED_PROPERTY_DTSTAMP, dtStamp); 635de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 636de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 637de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (isValidEventValues(cv)) { 638de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.set(eventOffset, 639de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon new Operation(ContentProviderOperation 640de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .newInsert(mAsSyncAdapterEvents).withValues(cv))); 641de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 642de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If we can't add this event (it's invalid), remove all of the inserts 643de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // we've built for it 644de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int cnt = ops.mCount - eventOffset; 645de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog(TAG, "Removing " + cnt + " inserts from mOps"); 646de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (int i = 0; i < cnt; i++) { 647de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.remove(eventOffset); 648de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 649de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.mCount = eventOffset; 650de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If this is a change, we need to also remove the deletion that comes 651de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // before the addition 652de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (deleteOffset >= 0) { 653de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Remove the deletion 654de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.remove(deleteOffset); 655de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // And the deletion of exceptions 656de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.remove(deleteOffset); 657de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog(TAG, "Removing deletion ops from mOps"); 658de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.mCount = deleteOffset; 659de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 660de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 661de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 662de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Mark the end of the event 663de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addSeparatorOperation(ops, Events.CONTENT_URI); 664de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 665de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 666de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private void logEventColumns(ContentValues cv, String reason) { 667de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (Eas.USER_LOG) { 668de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon StringBuilder sb = 669de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon new StringBuilder("Event invalid, " + reason + ", skipping: Columns = "); 670de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (Entry<String, Object> entry: cv.valueSet()) { 671de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon sb.append(entry.getKey()); 672de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon sb.append('/'); 673de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 674de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog(TAG, sb.toString()); 675de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 676de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 677de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 678de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /*package*/ boolean isValidEventValues(ContentValues cv) { 679de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean isException = cv.containsKey(Events.ORIGINAL_INSTANCE_TIME); 680de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // All events require DTSTART 681de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (!cv.containsKey(Events.DTSTART)) { 682de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon logEventColumns(cv, "DTSTART missing"); 683de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return false; 684de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If we're a top-level event, we must have _SYNC_DATA (uid) 685de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (!isException && !cv.containsKey(Events.SYNC_DATA2)) { 686de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon logEventColumns(cv, "_SYNC_DATA missing"); 687de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return false; 688de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We must also have DTEND or DURATION if we're not an exception 689de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (!isException && !cv.containsKey(Events.DTEND) && 690de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon !cv.containsKey(Events.DURATION)) { 691de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon logEventColumns(cv, "DTEND/DURATION missing"); 692de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return false; 693de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Exceptions require DTEND 694de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (isException && !cv.containsKey(Events.DTEND)) { 695de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon logEventColumns(cv, "Exception missing DTEND"); 696de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return false; 697de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If this is a recurrence, we need a DURATION (in days if an all-day event) 698de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (cv.containsKey(Events.RRULE)) { 699de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String duration = cv.getAsString(Events.DURATION); 700de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (duration == null) return false; 701de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (cv.containsKey(Events.ALL_DAY)) { 702de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Integer ade = cv.getAsInteger(Events.ALL_DAY); 703de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (ade != null && ade != 0 && !duration.endsWith("D")) { 704de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return false; 705de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 706de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 707de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 708de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return true; 709de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 710de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 711de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public String recurrenceParser() throws IOException { 712de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Turn this information into an RRULE 713de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int type = -1; 714de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int occurrences = -1; 715de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int interval = -1; 716de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int dow = -1; 717de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int dom = -1; 718de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int wom = -1; 719de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int moy = -1; 720de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String until = null; 721de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 722de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_RECURRENCE) != END) { 723de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 724de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_TYPE: 725de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon type = getValueInt(); 726de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 727de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_INTERVAL: 728de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon interval = getValueInt(); 729de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 730de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_OCCURRENCES: 731de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon occurrences = getValueInt(); 732de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 733de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_DAYOFWEEK: 734de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon dow = getValueInt(); 735de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 736de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_DAYOFMONTH: 737de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon dom = getValueInt(); 738de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 739de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_WEEKOFMONTH: 740de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon wom = getValueInt(); 741de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 742de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_MONTHOFYEAR: 743de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon moy = getValueInt(); 744de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 745de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE_UNTIL: 746de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon until = getValue(); 747de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 748de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 749de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 750de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 751de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 752de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 753de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return CalendarUtilities.rruleFromRecurrence(type, occurrences, interval, 754de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon dow, dom, wom, moy, until); 755de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 756de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 757de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private void exceptionParser(CalendarOperations ops, ContentValues parentCv, 758de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<ContentValues> attendeeValues, int reminderMins, int busyStatus, 759de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long startTime, long endTime) throws IOException { 760de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues cv = new ContentValues(); 761de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.CALENDAR_ID, mCalendarId); 762de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 763de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // It appears that these values have to be copied from the parent if they are to appear 764de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Note that they can be overridden below 765de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORGANIZER, parentCv.getAsString(Events.ORGANIZER)); 766de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.TITLE, parentCv.getAsString(Events.TITLE)); 767de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DESCRIPTION, parentCv.getAsString(Events.DESCRIPTION)); 768de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORIGINAL_ALL_DAY, parentCv.getAsInteger(Events.ALL_DAY)); 769de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_LOCATION, parentCv.getAsString(Events.EVENT_LOCATION)); 770de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ACCESS_LEVEL, parentCv.getAsString(Events.ACCESS_LEVEL)); 771de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_TIMEZONE, parentCv.getAsString(Events.EVENT_TIMEZONE)); 772de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Exceptions should always have this set to zero, since EAS has no concept of 773de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // separate attendee lists for exceptions; if we fail to do this, then the UI will 774de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // allow the user to change attendee data, and this change would never get reflected 775de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // on the server. 776de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.HAS_ATTENDEE_DATA, 0); 777de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 778de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int allDayEvent = 0; 779de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 780de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // This column is the key that links the exception to the serverId 781de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORIGINAL_SYNC_ID, parentCv.getAsString(Events._SYNC_ID)); 782de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 783de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String exceptionStartTime = "_noStartTime"; 784de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 785de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 786de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTACHMENTS: 787de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attachmentsParser(); 788de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 789de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_EXCEPTION_START_TIME: 790de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon exceptionStartTime = getValue(); 791de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ORIGINAL_INSTANCE_TIME, 792de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Utility.parseDateTimeToMillis(exceptionStartTime)); 793de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 794de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_EXCEPTION_IS_DELETED: 795de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (getValueInt() == 1) { 796de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.STATUS, Events.STATUS_CANCELED); 797de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 798de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 799de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ALL_DAY_EVENT: 800de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon allDayEvent = getValueInt(); 801de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ALL_DAY, allDayEvent); 802de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 803de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.BASE_BODY: 804de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DESCRIPTION, bodyParser()); 805de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 806de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_BODY: 807de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.DESCRIPTION, getValue()); 808de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 809de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_START_TIME: 810de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon startTime = Utility.parseDateTimeToMillis(getValue()); 811de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 812de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_END_TIME: 813de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon endTime = Utility.parseDateTimeToMillis(getValue()); 814de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 815de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_LOCATION: 816de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.EVENT_LOCATION, getValue()); 817de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 818de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_RECURRENCE: 819de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String rrule = recurrenceParser(); 820de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (rrule != null) { 821de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.RRULE, rrule); 822de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 823de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 824de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_SUBJECT: 825de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.TITLE, getValue()); 826de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 827de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_SENSITIVITY: 828de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.ACCESS_LEVEL, encodeVisibility(getValueInt())); 829de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 830de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_BUSY_STATUS: 831de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon busyStatus = getValueInt(); 832de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Don't set selfAttendeeStatus or CalendarProvider will create a duplicate 833de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // attendee! 834de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 835de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // TODO How to handle these items that are linked to event id! 836de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// case Tags.CALENDAR_DTSTAMP: 837de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// ops.newExtendedProperty("dtstamp", getValue()); 838de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// break; 839de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// case Tags.CALENDAR_REMINDER_MINS_BEFORE: 840de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// ops.newReminder(getValueInt()); 841de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon// break; 842de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 843de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 844de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 845de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 846de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 847de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We need a _sync_id, but it can't be the parent's id, so we generate one 848de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events._SYNC_ID, parentCv.getAsString(Events._SYNC_ID) + '_' + 849de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon exceptionStartTime); 850de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 851de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Enforce CalendarProvider required properties 852de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon setTimeRelatedValues(cv, startTime, endTime, allDayEvent); 853de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 854de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Don't insert an invalid exception event 855de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (!isValidEventValues(cv)) return; 856de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 857de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Add the exception insert 858de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int exceptionStart = ops.mCount; 859de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newException(cv); 860de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Also add the attendees, because they need to be copied over from the parent event 861de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon boolean attendeesRedacted = false; 862de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (attendeeValues != null) { 863de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (ContentValues attValues: attendeeValues) { 864de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If this is the user, use his busy status for attendee status 865de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String attendeeEmail = attValues.getAsString(Attendees.ATTENDEE_EMAIL); 866de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Note that the exception at which we surpass the redaction limit might have 867de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // any number of attendees shown; since this is an edge case and a workaround, 868de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // it seems to be an acceptable implementation 869de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (mAccount.mEmailAddress.equalsIgnoreCase(attendeeEmail)) { 870de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attValues.put(Attendees.ATTENDEE_STATUS, 871de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus)); 872de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newAttendee(attValues, exceptionStart); 873de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (ops.size() < MAX_OPS_BEFORE_EXCEPTION_ATTENDEE_REDACTION) { 874de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newAttendee(attValues, exceptionStart); 875de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 876de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeesRedacted = true; 877de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 878de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 879de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 880de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // And add the parent's reminder value 881de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (reminderMins > 0) { 882de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.newReminder(reminderMins, exceptionStart); 883de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 884de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (attendeesRedacted) { 8853eef378426c7c88608f53f5a268baed40259ccf6Alon Albert LogUtils.d(TAG, "Attendees redacted in this exception"); 886de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 887de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 888de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 889de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static int encodeVisibility(int easVisibility) { 890de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int visibility = 0; 891de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch(easVisibility) { 892de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 0: 893de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon visibility = Events.ACCESS_DEFAULT; 894de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 895de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 1: 896de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon visibility = Events.ACCESS_PUBLIC; 897de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 898de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 2: 899de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon visibility = Events.ACCESS_PRIVATE; 900de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 901de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 3: 902de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon visibility = Events.ACCESS_CONFIDENTIAL; 903de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 904de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 905de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return visibility; 906de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 907de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 908de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private void exceptionsParser(CalendarOperations ops, ContentValues cv, 909de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<ContentValues> attendeeValues, int reminderMins, int busyStatus, 910de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long startTime, long endTime) throws IOException { 911de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_EXCEPTIONS) != END) { 912de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 913de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_EXCEPTION: 914de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon exceptionParser(ops, cv, attendeeValues, reminderMins, busyStatus, 915de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon startTime, endTime); 916de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 917de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 918de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 919de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 920de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 921de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 922de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 923de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private String categoriesParser() throws IOException { 924de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon StringBuilder categories = new StringBuilder(); 925de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_CATEGORIES) != END) { 926de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 927de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_CATEGORY: 928de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // TODO Handle categories (there's no similar concept for gdata AFAIK) 929de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We need to save them and spit them back when we update the event 930de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon categories.append(getValue()); 931de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon categories.append(CATEGORY_TOKENIZER_DELIMITER); 932de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 933de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 934de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 935de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 936de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 937de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return categories.toString(); 938de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 939de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 940de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 941de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * For now, we ignore (but still have to parse) event attachments; these are new in EAS 14 942de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 943de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private void attachmentsParser() throws IOException { 944de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_ATTACHMENTS) != END) { 945de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 946de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTACHMENT: 947de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipParser(Tags.CALENDAR_ATTACHMENT); 948de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 949de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 950de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 951de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 952de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 953de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 954de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 955de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private ArrayList<ContentValues> attendeesParser() 956de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throws IOException { 957de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int attendeeCount = 0; 958de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<ContentValues> attendeeValues = new ArrayList<ContentValues>(); 959de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_ATTENDEES) != END) { 960de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 961de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEE: 962de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues cv = attendeeParser(); 963de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // If we're going to redact these attendees anyway, let's avoid unnecessary 964de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // memory pressure, and not keep them around 965de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // We still need to parse them all, however 966de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeCount++; 967de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Allow one more than MAX_ATTENDEES, so that the check for "too many" will 968de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // succeed in addEvent 969de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (attendeeCount <= (MAX_SYNCED_ATTENDEES+1)) { 970de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon attendeeValues.add(cv); 971de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 972de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 973de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 974de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 975de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 976de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 977de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return attendeeValues; 978de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 979de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 980de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private ContentValues attendeeParser() 981de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throws IOException { 982de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues cv = new ContentValues(); 983de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.CALENDAR_ATTENDEE) != END) { 984de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 985de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEE_EMAIL: 986de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.ATTENDEE_EMAIL, getValue()); 987de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 988de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEE_NAME: 989de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.ATTENDEE_NAME, getValue()); 990de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 991de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEE_STATUS: 992de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int status = getValueInt(); 993de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.ATTENDEE_STATUS, 994de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon (status == 2) ? Attendees.ATTENDEE_STATUS_TENTATIVE : 995de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon (status == 3) ? Attendees.ATTENDEE_STATUS_ACCEPTED : 996de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon (status == 4) ? Attendees.ATTENDEE_STATUS_DECLINED : 997de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon (status == 5) ? Attendees.ATTENDEE_STATUS_INVITED : 998de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Attendees.ATTENDEE_STATUS_NONE); 999de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1000de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.CALENDAR_ATTENDEE_TYPE: 1001de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int type = Attendees.TYPE_NONE; 1002de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // EAS types: 1 = req'd, 2 = opt, 3 = resource 1003de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (getValueInt()) { 1004de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 1: 1005de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon type = Attendees.TYPE_REQUIRED; 1006de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1007de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case 2: 1008de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon type = Attendees.TYPE_OPTIONAL; 1009de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1010de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1011de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.ATTENDEE_TYPE, type); 1012de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1013de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1014de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1015de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1016de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1017de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); 1018de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return cv; 1019de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1020de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1021de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private String bodyParser() throws IOException { 1022de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String body = null; 1023de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.BASE_BODY) != END) { 1024de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1025de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.BASE_DATA: 1026de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon body = getValue(); 1027de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1028de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1029de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1030de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1031de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1032de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1033de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Handle null data without error 1034de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (body == null) return ""; 1035de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Remove \r's from any body text 1036de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return body.replace("\r\n", "\n"); 1037de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1038de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1039de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void addParser(CalendarOperations ops) throws IOException { 1040de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String serverId = null; 1041de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_ADD) != END) { 1042de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1043de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_SERVER_ID: // same as 1044de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon serverId = getValue(); 1045de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1046de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_APPLICATION_DATA: 1047de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addEvent(ops, serverId, false); 1048de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1049de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1050de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1051de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1052de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1053de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1054de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1055de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private Cursor getServerIdCursor(String serverId) { 1056de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return mContentResolver.query(Events.CONTENT_URI, ID_PROJECTION, 1057de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon SERVER_ID_AND_CALENDAR_ID, new String[] {serverId, Long.toString(mCalendarId)}, 1058de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon null); 1059de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1060de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1061de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private Cursor getClientIdCursor(String clientId) { 1062de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mBindArgument[0] = clientId; 1063de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return mContentResolver.query(Events.CONTENT_URI, ID_PROJECTION, CLIENT_ID_SELECTION, 1064de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mBindArgument, null); 1065de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1066de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1067de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void deleteParser(CalendarOperations ops) throws IOException { 1068de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_DELETE) != END) { 1069de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1070de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_SERVER_ID: 1071de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String serverId = getValue(); 1072de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Find the event with the given serverId 1073de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Cursor c = getServerIdCursor(serverId); 1074de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 1075de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c.moveToFirst()) { 1076de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Deleting ", serverId); 1077de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.delete(c.getLong(0), serverId); 1078de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1079de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } finally { 1080de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon c.close(); 1081de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1082de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1083de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1084de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1085de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1086de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1087de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1088de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1089de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1090de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * A change is handled as a delete (including all exceptions) and an add 1091de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * This isn't as efficient as attempting to traverse the original and all of its exceptions, 1092de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * but changes happen infrequently and this code is both simpler and easier to maintain 1093de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @param ops the array of pending ContactProviderOperations. 1094de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * @throws IOException 1095de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1096de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void changeParser(CalendarOperations ops) throws IOException { 1097de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String serverId = null; 1098de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_CHANGE) != END) { 1099de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1100de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_SERVER_ID: 1101de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon serverId = getValue(); 1102de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1103de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_APPLICATION_DATA: 1104de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Changing " + serverId); 1105de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addEvent(ops, serverId, true); 1106de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1107de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1108de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1109de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1110de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1111de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1112de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1113de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon @Override 1114de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void commandsParser() throws IOException { 1115de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_COMMANDS) != END) { 1116de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (tag == Tags.SYNC_ADD) { 1117de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addParser(mOps); 1118de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (tag == Tags.SYNC_DELETE) { 1119de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon deleteParser(mOps); 1120de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (tag == Tags.SYNC_CHANGE) { 1121de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon changeParser(mOps); 1122de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else 1123de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1124de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1125de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1126de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1127de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon @Override 1128de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void commit() throws IOException { 1129de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Calendar SyncKey saved as: ", mMailbox.mSyncKey); 1130de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Save the syncKey here, using the Helper provider by Calendar provider 1131de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mOps.add(new Operation(SyncStateContract.Helpers.newSetOperation( 1132de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon asSyncAdapter(SyncState.CONTENT_URI, mAccount.mEmailAddress, 1133de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), 1134de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mAccountManagerAccount, 1135de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mMailbox.mSyncKey.getBytes()))); 1136de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1137de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Execute our CPO's safely 1138de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 1139de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon safeExecute(mContentResolver, CalendarContract.AUTHORITY, mOps); 1140de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } catch (RemoteException e) { 1141de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throw new IOException("Remote exception caught; will retry"); 1142de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1143de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1144de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1145de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void addResponsesParser() throws IOException { 1146de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String serverId = null; 1147de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String clientId = null; 1148de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int status = -1; 1149de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentValues cv = new ContentValues(); 1150de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_ADD) != END) { 1151de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1152de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_SERVER_ID: 1153de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon serverId = getValue(); 1154de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1155de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_CLIENT_ID: 1156de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon clientId = getValue(); 1157de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1158de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_STATUS: 1159de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon status = getValueInt(); 1160de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (status != 1) { 1161de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Attempt to add event failed with status: " + status); 1162de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1163de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1164de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1165de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1166de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1167de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1168de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1169de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (clientId == null) return; 1170de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (serverId == null) { 1171de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // TODO Reconsider how to handle this 1172de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon serverId = "FAIL:" + status; 1173de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1174de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1175de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Cursor c = getClientIdCursor(clientId); 1176de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 1177de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (c.moveToFirst()) { 1178de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events._SYNC_ID, serverId); 1179de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cv.put(Events.SYNC_DATA2, clientId); 1180de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon long id = c.getLong(0); 1181de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Write the serverId into the Event 1182de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mOps.add(new Operation(ContentProviderOperation 1183de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .newUpdate(ContentUris.withAppendedId(mAsSyncAdapterEvents, id)) 1184de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon .withValues(cv))); 1185de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("New event " + clientId + " was given serverId: " + serverId); 1186de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1187de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } finally { 1188de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon c.close(); 1189de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1190de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1191de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1192de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void changeResponsesParser() throws IOException { 1193de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String serverId = null; 1194de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon String status = null; 1195de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_CHANGE) != END) { 1196de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon switch (tag) { 1197de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_SERVER_ID: 1198de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon serverId = getValue(); 1199de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1200de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon case Tags.SYNC_STATUS: 1201de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon status = getValue(); 1202de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon break; 1203de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon default: 1204de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1205de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1206de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1207de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (serverId != null && status != null) { 1208de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon userLog("Changed event " + serverId + " failed with status: " + status); 1209de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1210de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1211de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1212de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1213de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon @Override 1214de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon public void responsesParser() throws IOException { 1215de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Handle server responses here (for Add and Change) 1216de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon while (nextTag(Tags.SYNC_RESPONSES) != END) { 1217de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (tag == Tags.SYNC_ADD) { 1218de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon addResponsesParser(); 1219de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (tag == Tags.SYNC_CHANGE) { 1220de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon changeResponsesParser(); 1221de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else 1222de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon skipTag(); 1223de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1224de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1225de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1226de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1227de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * We apply the batch of CPO's here. We synchronize on the service to avoid thread-nasties, 1228de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * and we just return quickly if the service has already been stopped. 1229de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1230de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static ContentProviderResult[] execute(final ContentResolver contentResolver, 1231de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final String authority, final ArrayList<ContentProviderOperation> ops) 1232de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throws RemoteException, OperationApplicationException { 1233de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (!ops.isEmpty()) { 1234de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentProviderResult[] result = contentResolver.applyBatch(authority, ops); 1235de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon //mService.userLog("Results: " + result.length); 1236de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return result; 1237de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1238de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return new ContentProviderResult[0]; 1239de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1240de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1241de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1242de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Convert an Operation to a CPO; if the Operation has a back reference, apply it with the 1243de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * passed-in offset 1244de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1245de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon @VisibleForTesting 1246de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon static ContentProviderOperation operationToContentProviderOperation(Operation op, int offset) { 1247de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (op.mOp != null) { 1248de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return op.mOp; 1249de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else if (op.mBuilder == null) { 1250de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throw new IllegalArgumentException("Operation must have CPO.Builder"); 1251de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1252de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentProviderOperation.Builder builder = op.mBuilder; 1253de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (op.mColumnName != null) { 1254de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon builder.withValueBackReference(op.mColumnName, op.mOffset - offset); 1255de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1256de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return builder.build(); 1257de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1258de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1259de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1260de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Create a list of CPOs from a list of Operations, and then apply them in a batch 1261de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1262de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static ContentProviderResult[] applyBatch(final ContentResolver contentResolver, 1263de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final String authority, final ArrayList<Operation> ops, final int offset) 1264de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throws RemoteException, OperationApplicationException { 1265de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Handle the empty case 1266de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (ops.isEmpty()) { 1267de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return new ContentProviderResult[0]; 1268de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1269de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<ContentProviderOperation> cpos = new ArrayList<ContentProviderOperation>(); 1270de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (Operation op: ops) { 1271de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon cpos.add(operationToContentProviderOperation(op, offset)); 1272de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1273de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return execute(contentResolver, authority, cpos); 1274de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1275de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1276de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1277de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Apply the list of CPO's in the provider and copy the "mini" result into our full result array 1278de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1279de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon private static void applyAndCopyResults(final ContentResolver contentResolver, 1280de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final String authority, final ArrayList<Operation> mini, 1281de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final ContentProviderResult[] result, final int offset) throws RemoteException { 1282de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Empty lists are ok; we just ignore them 1283de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (mini.isEmpty()) return; 1284de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 1285de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentProviderResult[] miniResult = applyBatch(contentResolver, authority, mini, 1286de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon offset); 1287de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Copy the results from this mini-batch into our results array 1288de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon System.arraycopy(miniResult, 0, result, offset, miniResult.length); 1289de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } catch (OperationApplicationException e) { 1290de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Not possible since we're building the ops ourselves 1291de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1292de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1293de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1294de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1295de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Called by a sync adapter to execute a list of Operations in the ContentProvider handling 1296de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * the passed-in authority. If the attempt to apply the batch fails due to a too-large 1297de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * binder transaction, we split the Operations as directed by separators. If any of the 1298de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * "mini" batches fails due to a too-large transaction, we're screwed, but this would be 1299de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * vanishingly rare. Other, possibly transient, errors are handled by throwing a 1300de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * RemoteException, which the caller will likely re-throw as an IOException so that the sync 1301de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * can be attempted again. 1302de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * 1303de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Callers MAY leave a dangling separator at the end of the list; note that the separators 1304de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * themselves are only markers and are not sent to the provider. 1305de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1306de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon protected static ContentProviderResult[] safeExecute(final ContentResolver contentResolver, 1307de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon final String authority, final ArrayList<Operation> ops) throws RemoteException { 1308de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon //mService.userLog("Try to execute ", ops.size(), " CPO's for " + authority); 1309de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentProviderResult[] result = null; 1310de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon try { 1311de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Try to execute the whole thing 1312de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return applyBatch(contentResolver, authority, ops, 0); 1313de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } catch (TransactionTooLargeException e) { 1314de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Nope; split into smaller chunks, demarcated by the separator operation 1315de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon //mService.userLog("Transaction too large; spliting!"); 1316de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ArrayList<Operation> mini = new ArrayList<Operation>(); 1317de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Build a result array with the total size we're sending 1318de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon result = new ContentProviderResult[ops.size()]; 1319de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int count = 0; 1320de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int offset = 0; 1321de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon for (Operation op: ops) { 1322de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if (op.mSeparator) { 1323add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert //mService.userLog("Try mini-batch of ", mini.size(), " CPO's"); 1324add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert applyAndCopyResults(contentResolver, authority, mini, result, offset); 1325add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert mini.clear(); 1326add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert // Save away the offset here; this will need to be subtracted out of the 1327add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert // value originally set by the adapter 1328add575db94f86397c9246d89ed85d29968b5aeaaAlon Albert offset = count + 1; // Remember to add 1 for the separator! 1329de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } else { 1330de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon mini.add(op); 1331de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1332de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon count++; 1333de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1334de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Check out what's left; if it's more than just a separator, apply the batch 1335de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon int miniSize = mini.size(); 1336de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon if ((miniSize > 0) && !(miniSize == 1 && mini.get(0).mSeparator)) { 1337de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon applyAndCopyResults(contentResolver, authority, mini, result, offset); 1338de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1339de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } catch (RemoteException e) { 1340de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon throw e; 1341de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } catch (OperationApplicationException e) { 1342de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon // Not possible since we're building the ops ourselves 1343de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1344de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon return result; 1345de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1346de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 1347de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon /** 1348de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon * Called by a sync adapter to indicate a relatively safe place to split a batch of CPO's 1349de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon */ 1350de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon protected static void addSeparatorOperation(ArrayList<Operation> ops, Uri uri) { 1351de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon Operation op = new Operation( 1352de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ContentProviderOperation.newDelete(ContentUris.withAppendedId(uri, SEPARATOR_ID))); 1353de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon op.mSeparator = true; 1354de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon ops.add(op); 1355de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon } 1356de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon 135728f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon @Override 135828f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon protected void wipe() { 135928f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon LogUtils.w(TAG, "Wiping calendar for account %d", mAccount.mId); 136028f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon EasCalendarSyncHandler.wipeAccountFromContentProvider(mContext, 136128f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon mAccount.mEmailAddress); 136228f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon } 1363de6b8b15730f59f3178e4a4223ddf00c6acd16c2Martin Hibdon} 1364