EditEventHelper.java revision 79f228124de7d98146ca526d743436f6419e2365
173714169d34a55fc01e14b30897a1ebbacf9934eErik/* 273714169d34a55fc01e14b30897a1ebbacf9934eErik * Copyright (C) 2010 The Android Open Source Project 373714169d34a55fc01e14b30897a1ebbacf9934eErik * 473714169d34a55fc01e14b30897a1ebbacf9934eErik * Licensed under the Apache License, Version 2.0 (the "License"); 573714169d34a55fc01e14b30897a1ebbacf9934eErik * you may not use this file except in compliance with the License. 673714169d34a55fc01e14b30897a1ebbacf9934eErik * You may obtain a copy of the License at 773714169d34a55fc01e14b30897a1ebbacf9934eErik * 873714169d34a55fc01e14b30897a1ebbacf9934eErik * http://www.apache.org/licenses/LICENSE-2.0 973714169d34a55fc01e14b30897a1ebbacf9934eErik * 1073714169d34a55fc01e14b30897a1ebbacf9934eErik * Unless required by applicable law or agreed to in writing, software 1173714169d34a55fc01e14b30897a1ebbacf9934eErik * distributed under the License is distributed on an "AS IS" BASIS, 1273714169d34a55fc01e14b30897a1ebbacf9934eErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1373714169d34a55fc01e14b30897a1ebbacf9934eErik * See the License for the specific language governing permissions and 1473714169d34a55fc01e14b30897a1ebbacf9934eErik * limitations under the License. 1573714169d34a55fc01e14b30897a1ebbacf9934eErik */ 1673714169d34a55fc01e14b30897a1ebbacf9934eErik 1773714169d34a55fc01e14b30897a1ebbacf9934eErikpackage com.android.calendar; 1873714169d34a55fc01e14b30897a1ebbacf9934eErik 1973714169d34a55fc01e14b30897a1ebbacf9934eErikimport com.android.common.Rfc822Validator; 2073714169d34a55fc01e14b30897a1ebbacf9934eErik 21eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentProviderOperation; 22eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentUris; 23eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentValues; 2473714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.content.res.Resources; 2573714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.database.Cursor; 2673714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.net.Uri; 2773714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.pim.EventRecurrence; 2873714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Attendees; 29eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.provider.Calendar.Calendars; 3073714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Events; 3173714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Reminders; 3273714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.TextUtils; 3373714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.format.DateUtils; 3473714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.format.Time; 3573714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.util.Rfc822Token; 3673714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.util.Rfc822Tokenizer; 3773714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.util.Log; 3873714169d34a55fc01e14b30897a1ebbacf9934eErik 39eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.ArrayList; 40eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.Calendar; 41eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.HashSet; 42eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.Iterator; 43eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.LinkedHashSet; 44eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.TimeZone; 4573714169d34a55fc01e14b30897a1ebbacf9934eErik 4673714169d34a55fc01e14b30897a1ebbacf9934eErikpublic class EditEventHelper { 4773714169d34a55fc01e14b30897a1ebbacf9934eErik private static final String TAG = "EditEventHelper"; 48eca82e9468c002590045338f98588ad3ce1a0bb3Erik 4973714169d34a55fc01e14b30897a1ebbacf9934eErik private static final boolean DEBUG = false; 5073714169d34a55fc01e14b30897a1ebbacf9934eErik 51eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int MAX_REMINDERS = 5; 52eca82e9468c002590045338f98588ad3ce1a0bb3Erik 5373714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final String[] EVENT_PROJECTION = new String[] { 5473714169d34a55fc01e14b30897a1ebbacf9934eErik Events._ID, // 0 5573714169d34a55fc01e14b30897a1ebbacf9934eErik Events.TITLE, // 1 5673714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DESCRIPTION, // 2 5773714169d34a55fc01e14b30897a1ebbacf9934eErik Events.EVENT_LOCATION, // 3 5873714169d34a55fc01e14b30897a1ebbacf9934eErik Events.ALL_DAY, // 4 5973714169d34a55fc01e14b30897a1ebbacf9934eErik Events.HAS_ALARM, // 5 6073714169d34a55fc01e14b30897a1ebbacf9934eErik Events.CALENDAR_ID, // 6 6173714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DTSTART, // 7 6273714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DTEND, // 8 6373714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DURATION, // 9 6473714169d34a55fc01e14b30897a1ebbacf9934eErik Events.EVENT_TIMEZONE, // 10 6573714169d34a55fc01e14b30897a1ebbacf9934eErik Events.RRULE, // 11 6673714169d34a55fc01e14b30897a1ebbacf9934eErik Events._SYNC_ID, // 12 6773714169d34a55fc01e14b30897a1ebbacf9934eErik Events.TRANSPARENCY, // 13 6873714169d34a55fc01e14b30897a1ebbacf9934eErik Events.VISIBILITY, // 14 6973714169d34a55fc01e14b30897a1ebbacf9934eErik Events.OWNER_ACCOUNT, // 15 7073714169d34a55fc01e14b30897a1ebbacf9934eErik Events.HAS_ATTENDEE_DATA, // 16 71eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.ORIGINAL_EVENT, // 17 7273714169d34a55fc01e14b30897a1ebbacf9934eErik }; 7373714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_ID = 0; 7473714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TITLE = 1; 7573714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DESCRIPTION = 2; 7673714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_EVENT_LOCATION = 3; 7773714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_ALL_DAY = 4; 7873714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_HAS_ALARM = 5; 7973714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_CALENDAR_ID = 6; 8073714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DTSTART = 7; 8173714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DTEND = 8; 8273714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DURATION = 9; 8373714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TIMEZONE = 10; 8473714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_RRULE = 11; 8573714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_SYNC_ID = 12; 8673714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TRANSPARENCY = 13; 8773714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_VISIBILITY = 14; 8873714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_OWNER_ACCOUNT = 15; 8973714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 16; 90eca82e9468c002590045338f98588ad3ce1a0bb3Erik protected static final int EVENT_INDEX_ORIGINAL_EVENT = 17; 9173714169d34a55fc01e14b30897a1ebbacf9934eErik 92eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String[] REMINDERS_PROJECTION = new String[] { 9373714169d34a55fc01e14b30897a1ebbacf9934eErik Reminders._ID, // 0 9473714169d34a55fc01e14b30897a1ebbacf9934eErik Reminders.MINUTES, // 1 95eca82e9468c002590045338f98588ad3ce1a0bb3Erik Reminders.METHOD, // 2 9673714169d34a55fc01e14b30897a1ebbacf9934eErik }; 97eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int REMINDERS_INDEX_MINUTES = 1; 98eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int REMINDERS_INDEX_METHOD = 2; 99eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=? AND (" + Reminders.METHOD 100eca82e9468c002590045338f98588ad3ce1a0bb3Erik + "=?" + " OR " + Reminders.METHOD + "=?" + ")"; 101eca82e9468c002590045338f98588ad3ce1a0bb3Erik 10273714169d34a55fc01e14b30897a1ebbacf9934eErik // Visible for testing 103eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String ATTENDEES_DELETE_PREFIX = Attendees.EVENT_ID + "=? AND " 104eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Attendees.ATTENDEE_EMAIL + " IN ("; 10573714169d34a55fc01e14b30897a1ebbacf9934eErik 10673714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int DOES_NOT_REPEAT = 0; 10773714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_DAILY = 1; 10873714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_EVERY_WEEKDAY = 2; 10973714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_WEEKLY_ON_DAY = 3; 11073714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_MONTHLY_ON_DAY_COUNT = 4; 11173714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_MONTHLY_ON_DAY = 5; 11273714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_YEARLY = 6; 11373714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_CUSTOM = 7; 11473714169d34a55fc01e14b30897a1ebbacf9934eErik 11573714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int MODIFY_UNINITIALIZED = 0; 11673714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int MODIFY_SELECTED = 1; 1178d69cd014d912e38d2ab37764aaf4aa7700c078bErik protected static final int MODIFY_ALL_FOLLOWING = 2; 1188d69cd014d912e38d2ab37764aaf4aa7700c078bErik protected static final int MODIFY_ALL = 3; 11973714169d34a55fc01e14b30897a1ebbacf9934eErik 12073714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int DAY_IN_SECONDS = 24 * 60 * 60; 12173714169d34a55fc01e14b30897a1ebbacf9934eErik 12237960c091b5912dd45beb561a85a9064ea42e616Erik private AbstractCalendarActivity mActivity; 12337960c091b5912dd45beb561a85a9064ea42e616Erik private AsyncQueryService mService; 124eca82e9468c002590045338f98588ad3ce1a0bb3Erik 125eca82e9468c002590045338f98588ad3ce1a0bb3Erik // public int mModification; 12673714169d34a55fc01e14b30897a1ebbacf9934eErik private Rfc822Validator mEmailValidator; 127eca82e9468c002590045338f98588ad3ce1a0bb3Erik 128eca82e9468c002590045338f98588ad3ce1a0bb3Erik // This allows us to flag the event if something is wrong with it, right now 129eca82e9468c002590045338f98588ad3ce1a0bb3Erik // if an uri is provided for an event that doesn't exist in the db. 13073714169d34a55fc01e14b30897a1ebbacf9934eErik protected boolean mEventOk = true; 13173714169d34a55fc01e14b30897a1ebbacf9934eErik 132eca82e9468c002590045338f98588ad3ce1a0bb3Erik /** 133eca82e9468c002590045338f98588ad3ce1a0bb3Erik * This is the symbolic name for the key used to pass in the boolean for 134eca82e9468c002590045338f98588ad3ce1a0bb3Erik * creating all-day events that is part of the extra data of the intent. 135eca82e9468c002590045338f98588ad3ce1a0bb3Erik * This is used only for creating new events and is set to true if the 136eca82e9468c002590045338f98588ad3ce1a0bb3Erik * default for the new event should be an all-day event. 137eca82e9468c002590045338f98588ad3ce1a0bb3Erik */ 138eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String EVENT_ALL_DAY = "allDay"; 139eca82e9468c002590045338f98588ad3ce1a0bb3Erik 140eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String[] CALENDARS_PROJECTION = new String[] { 141eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars._ID, // 0 142eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.DISPLAY_NAME, // 1 143eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.OWNER_ACCOUNT, // 2 144eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.COLOR, // 3 145eca82e9468c002590045338f98588ad3ce1a0bb3Erik }; 146eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_ID = 0; 147eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_DISPLAY_NAME = 1; 148eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; 149eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_COLOR = 3; 150eca82e9468c002590045338f98588ad3ce1a0bb3Erik 151eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String CALENDARS_WHERE_WRITEABLE_VISIBLE = Calendars.ACCESS_LEVEL + ">=" 152eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Calendars.CONTRIBUTOR_ACCESS + " AND " + Calendars.SELECTED + "=1"; 153eca82e9468c002590045338f98588ad3ce1a0bb3Erik 154eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String[] ATTENDEES_PROJECTION = new String[] { 155eca82e9468c002590045338f98588ad3ce1a0bb3Erik Attendees.ATTENDEE_NAME, // 0 156eca82e9468c002590045338f98588ad3ce1a0bb3Erik Attendees.ATTENDEE_EMAIL, // 1 157eca82e9468c002590045338f98588ad3ce1a0bb3Erik Attendees.ATTENDEE_RELATIONSHIP, // 2 158eca82e9468c002590045338f98588ad3ce1a0bb3Erik }; 159eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int ATTENDEES_INDEX_NAME = 0; 160eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int ATTENDEES_INDEX_EMAIL = 1; 161eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int ATTENDEES_INDEX_RELATIONSHIP = 2; 162eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String ATTENDEES_WHERE_NOT_ORGANIZER = Attendees.EVENT_ID + "=? AND " 163eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Attendees.ATTENDEE_RELATIONSHIP + "<>" + Attendees.RELATIONSHIP_ORGANIZER; 164eca82e9468c002590045338f98588ad3ce1a0bb3Erik 16537960c091b5912dd45beb561a85a9064ea42e616Erik public EditEventHelper(AbstractCalendarActivity activity, CalendarEventModel model) { 16673714169d34a55fc01e14b30897a1ebbacf9934eErik mActivity = activity; 16737960c091b5912dd45beb561a85a9064ea42e616Erik mService = activity.getAsyncQueryService(); 1686ae7d69b90f22fd51ab677036ba987dbdc35a62dErik setDomainFromModel(model); 1696ae7d69b90f22fd51ab677036ba987dbdc35a62dErik } 1706ae7d69b90f22fd51ab677036ba987dbdc35a62dErik 171eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Sets up the email validator for the given model 1726ae7d69b90f22fd51ab677036ba987dbdc35a62dErik public void setDomainFromModel(CalendarEventModel model) { 17373714169d34a55fc01e14b30897a1ebbacf9934eErik String domain = "gmail.com"; 174eca82e9468c002590045338f98588ad3ce1a0bb3Erik if (model != null) { 17573714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerAccount = model.mOwnerAccount; 17673714169d34a55fc01e14b30897a1ebbacf9934eErik if (!TextUtils.isEmpty(ownerAccount)) { 17773714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerDomain = extractDomain(ownerAccount); 17873714169d34a55fc01e14b30897a1ebbacf9934eErik if (!TextUtils.isEmpty(ownerDomain)) { 17973714169d34a55fc01e14b30897a1ebbacf9934eErik domain = ownerDomain; 18073714169d34a55fc01e14b30897a1ebbacf9934eErik } 18173714169d34a55fc01e14b30897a1ebbacf9934eErik } 18273714169d34a55fc01e14b30897a1ebbacf9934eErik } 18373714169d34a55fc01e14b30897a1ebbacf9934eErik mEmailValidator = new Rfc822Validator(domain); 18473714169d34a55fc01e14b30897a1ebbacf9934eErik } 18573714169d34a55fc01e14b30897a1ebbacf9934eErik 18673714169d34a55fc01e14b30897a1ebbacf9934eErik /** 187eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the event. Returns true if the event was successfully saved, false 188eca82e9468c002590045338f98588ad3ce1a0bb3Erik * otherwise. 18973714169d34a55fc01e14b30897a1ebbacf9934eErik * 19073714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The event model to save 19173714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalModel A model of the original event if it exists 19273714169d34a55fc01e14b30897a1ebbacf9934eErik * @param modifyWhich For recurring events which type of series modification to use 19373714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if the event was successfully queued for saving 19473714169d34a55fc01e14b30897a1ebbacf9934eErik */ 19573714169d34a55fc01e14b30897a1ebbacf9934eErik public boolean saveEvent(CalendarEventModel model, CalendarEventModel originalModel, 19673714169d34a55fc01e14b30897a1ebbacf9934eErik int modifyWhich) { 19773714169d34a55fc01e14b30897a1ebbacf9934eErik boolean forceSaveReminders = false; 19873714169d34a55fc01e14b30897a1ebbacf9934eErik 19973714169d34a55fc01e14b30897a1ebbacf9934eErik if (DEBUG) { 20073714169d34a55fc01e14b30897a1ebbacf9934eErik Log.d(TAG, "Saving event model: " + model); 20173714169d34a55fc01e14b30897a1ebbacf9934eErik } 20273714169d34a55fc01e14b30897a1ebbacf9934eErik 20373714169d34a55fc01e14b30897a1ebbacf9934eErik if (!mEventOk) { 20473714169d34a55fc01e14b30897a1ebbacf9934eErik if (DEBUG) { 20573714169d34a55fc01e14b30897a1ebbacf9934eErik Log.w(TAG, "Event no longer exists. Event was not saved."); 20673714169d34a55fc01e14b30897a1ebbacf9934eErik } 20773714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 20873714169d34a55fc01e14b30897a1ebbacf9934eErik } 20973714169d34a55fc01e14b30897a1ebbacf9934eErik 21073714169d34a55fc01e14b30897a1ebbacf9934eErik // It's a problem if we try to save a non-existent or invalid model or if we're 21173714169d34a55fc01e14b30897a1ebbacf9934eErik // modifying an existing event and we have the wrong original model 21273714169d34a55fc01e14b30897a1ebbacf9934eErik if (model == null) { 21373714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Attempted to save null model."); 21473714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 21573714169d34a55fc01e14b30897a1ebbacf9934eErik } 21673714169d34a55fc01e14b30897a1ebbacf9934eErik if (!model.isValid()) { 21773714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Attempted to save invalid model."); 21873714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 21973714169d34a55fc01e14b30897a1ebbacf9934eErik } 22073714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null && !isSameEvent(model, originalModel)) { 221eca82e9468c002590045338f98588ad3ce1a0bb3Erik Log.e(TAG, "Attempted to update existing event but models didn't refer to the same " 222eca82e9468c002590045338f98588ad3ce1a0bb3Erik + "event."); 22373714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 22473714169d34a55fc01e14b30897a1ebbacf9934eErik } 22573714169d34a55fc01e14b30897a1ebbacf9934eErik 226eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO Check if anything has been changed and return false if it 227eca82e9468c002590045338f98588ad3ce1a0bb3Erik // hasn't. 22873714169d34a55fc01e14b30897a1ebbacf9934eErik 22973714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 23073714169d34a55fc01e14b30897a1ebbacf9934eErik int eventIdIndex = -1; 23173714169d34a55fc01e14b30897a1ebbacf9934eErik 23273714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = getContentValuesFromModel(model); 23373714169d34a55fc01e14b30897a1ebbacf9934eErik Uri uri = model.mUri; 23473714169d34a55fc01e14b30897a1ebbacf9934eErik 23573714169d34a55fc01e14b30897a1ebbacf9934eErik if (uri != null && originalModel == null) { 23673714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Existing event but no originalModel provided. Aborting save."); 23773714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 23873714169d34a55fc01e14b30897a1ebbacf9934eErik } 23973714169d34a55fc01e14b30897a1ebbacf9934eErik 24073714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the "hasAlarm" field for the event 24173714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> reminderMinutes = model.mReminderMinutes; 24273714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 24373714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0); 24473714169d34a55fc01e14b30897a1ebbacf9934eErik 24573714169d34a55fc01e14b30897a1ebbacf9934eErik if (uri == null) { 24673714169d34a55fc01e14b30897a1ebbacf9934eErik // Add hasAttendeeData for a new event 24773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.HAS_ATTENDEE_DATA, 1); 24873714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 249eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newInsert( 250eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.CONTENT_URI).withValues(values); 25173714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 25273714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 25373714169d34a55fc01e14b30897a1ebbacf9934eErik 25473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (model.mRrule == null && originalModel.mRrule == null) { 25573714169d34a55fc01e14b30897a1ebbacf9934eErik // Simple update to a non-recurring event 25673714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 25773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 25873714169d34a55fc01e14b30897a1ebbacf9934eErik 25973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (originalModel.mRrule == null) { 26073714169d34a55fc01e14b30897a1ebbacf9934eErik // This event was changed from a non-repeating event to a 26173714169d34a55fc01e14b30897a1ebbacf9934eErik // repeating event. 26273714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 26373714169d34a55fc01e14b30897a1ebbacf9934eErik 26473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_SELECTED) { 26573714169d34a55fc01e14b30897a1ebbacf9934eErik // Modify contents of the current instance of repeating event 26673714169d34a55fc01e14b30897a1ebbacf9934eErik // Create a recurrence exception 26773714169d34a55fc01e14b30897a1ebbacf9934eErik long begin = model.mOriginalStart; 26873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_EVENT, originalModel.mSyncId); 26973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_INSTANCE_TIME, begin); 27073714169d34a55fc01e14b30897a1ebbacf9934eErik boolean allDay = originalModel.mAllDay; 27173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0); 27273714169d34a55fc01e14b30897a1ebbacf9934eErik 27373714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 274eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newInsert( 275eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.CONTENT_URI).withValues(values); 27673714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 27773714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 27873714169d34a55fc01e14b30897a1ebbacf9934eErik 27973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_ALL_FOLLOWING) { 28073714169d34a55fc01e14b30897a1ebbacf9934eErik 28173714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mRrule == null) { 28273714169d34a55fc01e14b30897a1ebbacf9934eErik // We've changed a recurring event to a non-recurring event. 28373714169d34a55fc01e14b30897a1ebbacf9934eErik // If the event we are editing is the first in the series, 284eca82e9468c002590045338f98588ad3ce1a0bb3Erik // then delete the whole series. Otherwise, update the series 28573714169d34a55fc01e14b30897a1ebbacf9934eErik // to end at the new start time. 28673714169d34a55fc01e14b30897a1ebbacf9934eErik if (isFirstEventInSeries(model, originalModel)) { 28773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newDelete(uri).build()); 28873714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 28973714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the current repeating event to end at the new 29073714169d34a55fc01e14b30897a1ebbacf9934eErik // start time. 29173714169d34a55fc01e14b30897a1ebbacf9934eErik updatePastEvents(ops, originalModel, model.mOriginalStart); 29273714169d34a55fc01e14b30897a1ebbacf9934eErik } 29373714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 294eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values) 29573714169d34a55fc01e14b30897a1ebbacf9934eErik .build()); 29673714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 29773714169d34a55fc01e14b30897a1ebbacf9934eErik if (isFirstEventInSeries(model, originalModel)) { 29873714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 299eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate(uri) 30073714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 30173714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 30273714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 30373714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the current repeating event to end at the new 30473714169d34a55fc01e14b30897a1ebbacf9934eErik // start time. 30573714169d34a55fc01e14b30897a1ebbacf9934eErik updatePastEvents(ops, originalModel, model.mOriginalStart); 30673714169d34a55fc01e14b30897a1ebbacf9934eErik 30773714169d34a55fc01e14b30897a1ebbacf9934eErik // Create a new event with the user-modified fields 30873714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 309eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues( 310eca82e9468c002590045338f98588ad3ce1a0bb3Erik values).build()); 31173714169d34a55fc01e14b30897a1ebbacf9934eErik } 31273714169d34a55fc01e14b30897a1ebbacf9934eErik } 31373714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 31473714169d34a55fc01e14b30897a1ebbacf9934eErik 31573714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_ALL) { 31673714169d34a55fc01e14b30897a1ebbacf9934eErik 31773714169d34a55fc01e14b30897a1ebbacf9934eErik // Modify all instances of repeating event 31873714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mRrule == null) { 31973714169d34a55fc01e14b30897a1ebbacf9934eErik // We've changed a recurring event to a non-recurring event. 32073714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete the whole series and replace it with a new 32173714169d34a55fc01e14b30897a1ebbacf9934eErik // non-recurring event. 32273714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newDelete(uri).build()); 32373714169d34a55fc01e14b30897a1ebbacf9934eErik 32473714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 325eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values) 32673714169d34a55fc01e14b30897a1ebbacf9934eErik .build()); 32773714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 32873714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 32973714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 330eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 33173714169d34a55fc01e14b30897a1ebbacf9934eErik } 33273714169d34a55fc01e14b30897a1ebbacf9934eErik } 33373714169d34a55fc01e14b30897a1ebbacf9934eErik 33473714169d34a55fc01e14b30897a1ebbacf9934eErik // New Event or New Exception to an existing event 33573714169d34a55fc01e14b30897a1ebbacf9934eErik boolean newEvent = (eventIdIndex != -1); 33673714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> originalMinutes; 33773714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null) { 33873714169d34a55fc01e14b30897a1ebbacf9934eErik originalMinutes = originalModel.mReminderMinutes; 33973714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 34073714169d34a55fc01e14b30897a1ebbacf9934eErik originalMinutes = new ArrayList<Integer>(); 34173714169d34a55fc01e14b30897a1ebbacf9934eErik } 34273714169d34a55fc01e14b30897a1ebbacf9934eErik 34373714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent) { 344eca82e9468c002590045338f98588ad3ce1a0bb3Erik saveRemindersWithBackRef(ops, eventIdIndex, reminderMinutes, originalMinutes, 345eca82e9468c002590045338f98588ad3ce1a0bb3Erik forceSaveReminders); 34673714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (uri != null) { 34773714169d34a55fc01e14b30897a1ebbacf9934eErik long eventId = ContentUris.parseId(uri); 348eca82e9468c002590045338f98588ad3ce1a0bb3Erik saveReminders(ops, eventId, reminderMinutes, originalMinutes, forceSaveReminders); 34973714169d34a55fc01e14b30897a1ebbacf9934eErik } 35073714169d34a55fc01e14b30897a1ebbacf9934eErik 35173714169d34a55fc01e14b30897a1ebbacf9934eErik ContentProviderOperation.Builder b; 35273714169d34a55fc01e14b30897a1ebbacf9934eErik boolean hasAttendeeData = model.mHasAttendeeData; 35373714169d34a55fc01e14b30897a1ebbacf9934eErik 35473714169d34a55fc01e14b30897a1ebbacf9934eErik // New event/instance - Set Organizer's response as yes 35573714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasAttendeeData && newEvent) { 35673714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 35773714169d34a55fc01e14b30897a1ebbacf9934eErik 35873714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerEmail = model.mOwnerAccount; 35973714169d34a55fc01e14b30897a1ebbacf9934eErik if (ownerEmail != null) { 36073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.ATTENDEE_EMAIL, ownerEmail); 361eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER); 362eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 363eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED); 364eca82e9468c002590045338f98588ad3ce1a0bb3Erik 365eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values); 36673714169d34a55fc01e14b30897a1ebbacf9934eErik b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex); 36773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 36873714169d34a55fc01e14b30897a1ebbacf9934eErik } 36973714169d34a55fc01e14b30897a1ebbacf9934eErik } 37073714169d34a55fc01e14b30897a1ebbacf9934eErik 371eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO: is this the right test? this currently checks if this is 372eca82e9468c002590045338f98588ad3ce1a0bb3Erik // a new event or an existing event. or is this a paranoia check? 37373714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasAttendeeData && (newEvent || uri != null)) { 37473714169d34a55fc01e14b30897a1ebbacf9934eErik String attendees = model.mAttendees; 37573714169d34a55fc01e14b30897a1ebbacf9934eErik String originalAttendeesString; 37673714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null) { 37773714169d34a55fc01e14b30897a1ebbacf9934eErik originalAttendeesString = originalModel.mAttendees; 37873714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 37973714169d34a55fc01e14b30897a1ebbacf9934eErik originalAttendeesString = ""; 38073714169d34a55fc01e14b30897a1ebbacf9934eErik } 381eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Hit the content provider only if this is a new event or the user 382eca82e9468c002590045338f98588ad3ce1a0bb3Erik // has changed it 38373714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent || !TextUtils.equals(originalAttendeesString, attendees)) { 38473714169d34a55fc01e14b30897a1ebbacf9934eErik // figure out which attendees need to be added and which ones 385eca82e9468c002590045338f98588ad3ce1a0bb3Erik // need to be deleted. use a linked hash set, so we maintain 38673714169d34a55fc01e14b30897a1ebbacf9934eErik // order (but also remove duplicates). 387eca82e9468c002590045338f98588ad3ce1a0bb3Erik setDomainFromModel(model); 38873714169d34a55fc01e14b30897a1ebbacf9934eErik LinkedHashSet<Rfc822Token> newAttendees = getAddressesFromList(attendees); 38973714169d34a55fc01e14b30897a1ebbacf9934eErik 39073714169d34a55fc01e14b30897a1ebbacf9934eErik // the eventId is only used if eventIdIndex is -1. 39173714169d34a55fc01e14b30897a1ebbacf9934eErik // TODO: clean up this code. 39273714169d34a55fc01e14b30897a1ebbacf9934eErik long eventId = uri != null ? ContentUris.parseId(uri) : -1; 39373714169d34a55fc01e14b30897a1ebbacf9934eErik 39473714169d34a55fc01e14b30897a1ebbacf9934eErik // only compute deltas if this is an existing event. 39573714169d34a55fc01e14b30897a1ebbacf9934eErik // new events (being inserted into the Events table) won't 39673714169d34a55fc01e14b30897a1ebbacf9934eErik // have any existing attendees. 39773714169d34a55fc01e14b30897a1ebbacf9934eErik if (!newEvent) { 39873714169d34a55fc01e14b30897a1ebbacf9934eErik HashSet<Rfc822Token> removedAttendees = new HashSet<Rfc822Token>(); 39973714169d34a55fc01e14b30897a1ebbacf9934eErik HashSet<Rfc822Token> originalAttendees = new HashSet<Rfc822Token>(); 40073714169d34a55fc01e14b30897a1ebbacf9934eErik Rfc822Tokenizer.tokenize(originalAttendeesString, originalAttendees); 40173714169d34a55fc01e14b30897a1ebbacf9934eErik for (Rfc822Token originalAttendee : originalAttendees) { 40273714169d34a55fc01e14b30897a1ebbacf9934eErik if (newAttendees.contains(originalAttendee)) { 403eca82e9468c002590045338f98588ad3ce1a0bb3Erik // existing attendee. remove from new attendees set. 40473714169d34a55fc01e14b30897a1ebbacf9934eErik newAttendees.remove(originalAttendee); 40573714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 406eca82e9468c002590045338f98588ad3ce1a0bb3Erik // no longer in attendees. mark as removed. 40773714169d34a55fc01e14b30897a1ebbacf9934eErik removedAttendees.add(originalAttendee); 40873714169d34a55fc01e14b30897a1ebbacf9934eErik } 40973714169d34a55fc01e14b30897a1ebbacf9934eErik } 41073714169d34a55fc01e14b30897a1ebbacf9934eErik 41173714169d34a55fc01e14b30897a1ebbacf9934eErik // delete removed attendees if necessary 41273714169d34a55fc01e14b30897a1ebbacf9934eErik if (removedAttendees.size() > 0) { 41373714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newDelete(Attendees.CONTENT_URI); 41473714169d34a55fc01e14b30897a1ebbacf9934eErik 41573714169d34a55fc01e14b30897a1ebbacf9934eErik String[] args = new String[removedAttendees.size() + 1]; 41673714169d34a55fc01e14b30897a1ebbacf9934eErik args[0] = Long.toString(eventId); 41773714169d34a55fc01e14b30897a1ebbacf9934eErik int i = 1; 41873714169d34a55fc01e14b30897a1ebbacf9934eErik StringBuilder deleteWhere = new StringBuilder(ATTENDEES_DELETE_PREFIX); 41973714169d34a55fc01e14b30897a1ebbacf9934eErik for (Rfc822Token removedAttendee : removedAttendees) { 42073714169d34a55fc01e14b30897a1ebbacf9934eErik if (i > 1) { 42173714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append(","); 42273714169d34a55fc01e14b30897a1ebbacf9934eErik } 42373714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append("?"); 42473714169d34a55fc01e14b30897a1ebbacf9934eErik args[i++] = removedAttendee.getAddress(); 42573714169d34a55fc01e14b30897a1ebbacf9934eErik } 42673714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append(")"); 42773714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(deleteWhere.toString(), args); 42873714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 42973714169d34a55fc01e14b30897a1ebbacf9934eErik } 43073714169d34a55fc01e14b30897a1ebbacf9934eErik } 43173714169d34a55fc01e14b30897a1ebbacf9934eErik 43273714169d34a55fc01e14b30897a1ebbacf9934eErik if (newAttendees.size() > 0) { 43373714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new attendees 43473714169d34a55fc01e14b30897a1ebbacf9934eErik for (Rfc822Token attendee : newAttendees) { 43573714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 436eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_NAME, attendee.getName()); 437eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_EMAIL, attendee.getAddress()); 43873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.ATTENDEE_RELATIONSHIP, 43973714169d34a55fc01e14b30897a1ebbacf9934eErik Attendees.RELATIONSHIP_ATTENDEE); 440eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 441eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 44273714169d34a55fc01e14b30897a1ebbacf9934eErik 44373714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent) { 444eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI) 44573714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 44673714169d34a55fc01e14b30897a1ebbacf9934eErik b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex); 44773714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 44873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.EVENT_ID, eventId); 449eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI) 45073714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 45173714169d34a55fc01e14b30897a1ebbacf9934eErik } 45273714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 45373714169d34a55fc01e14b30897a1ebbacf9934eErik } 45473714169d34a55fc01e14b30897a1ebbacf9934eErik } 45573714169d34a55fc01e14b30897a1ebbacf9934eErik } 45673714169d34a55fc01e14b30897a1ebbacf9934eErik } 45773714169d34a55fc01e14b30897a1ebbacf9934eErik 45837960c091b5912dd45beb561a85a9064ea42e616Erik 45937960c091b5912dd45beb561a85a9064ea42e616Erik mService.startBatch(mService.getNextToken(), null, android.provider.Calendar.AUTHORITY, ops, 46037960c091b5912dd45beb561a85a9064ea42e616Erik Utils.UNDO_DELAY); 46173714169d34a55fc01e14b30897a1ebbacf9934eErik 46273714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 46373714169d34a55fc01e14b30897a1ebbacf9934eErik } 46473714169d34a55fc01e14b30897a1ebbacf9934eErik 46573714169d34a55fc01e14b30897a1ebbacf9934eErik LinkedHashSet<Rfc822Token> getAddressesFromList(String list) { 46673714169d34a55fc01e14b30897a1ebbacf9934eErik LinkedHashSet<Rfc822Token> addresses = new LinkedHashSet<Rfc822Token>(); 46773714169d34a55fc01e14b30897a1ebbacf9934eErik Rfc822Tokenizer.tokenize(list, addresses); 46873714169d34a55fc01e14b30897a1ebbacf9934eErik 469eca82e9468c002590045338f98588ad3ce1a0bb3Erik // validate the emails, out of paranoia. they should already be 47073714169d34a55fc01e14b30897a1ebbacf9934eErik // validated on input, but drop any invalid emails just to be safe. 47173714169d34a55fc01e14b30897a1ebbacf9934eErik Iterator<Rfc822Token> addressIterator = addresses.iterator(); 47273714169d34a55fc01e14b30897a1ebbacf9934eErik while (addressIterator.hasNext()) { 47373714169d34a55fc01e14b30897a1ebbacf9934eErik Rfc822Token address = addressIterator.next(); 47473714169d34a55fc01e14b30897a1ebbacf9934eErik if (!mEmailValidator.isValid(address.getAddress())) { 47573714169d34a55fc01e14b30897a1ebbacf9934eErik Log.v(TAG, "Dropping invalid attendee email address: " + address); 47673714169d34a55fc01e14b30897a1ebbacf9934eErik addressIterator.remove(); 47773714169d34a55fc01e14b30897a1ebbacf9934eErik } 47873714169d34a55fc01e14b30897a1ebbacf9934eErik } 47973714169d34a55fc01e14b30897a1ebbacf9934eErik return addresses; 48073714169d34a55fc01e14b30897a1ebbacf9934eErik } 48173714169d34a55fc01e14b30897a1ebbacf9934eErik 482eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO think about how useful this is. Probably check if our event has 483eca82e9468c002590045338f98588ad3ce1a0bb3Erik // changed early on and either update all or nothing. Should still do the if 484eca82e9468c002590045338f98588ad3ce1a0bb3Erik // MODIFY_ALL bit. 485eca82e9468c002590045338f98588ad3ce1a0bb3Erik void checkTimeDependentFields(CalendarEventModel originalModel, CalendarEventModel model, 486eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentValues values, int modifyWhich) { 48773714169d34a55fc01e14b30897a1ebbacf9934eErik long oldBegin = model.mOriginalStart; 48873714169d34a55fc01e14b30897a1ebbacf9934eErik long oldEnd = model.mOriginalEnd; 48973714169d34a55fc01e14b30897a1ebbacf9934eErik boolean oldAllDay = originalModel.mAllDay; 49073714169d34a55fc01e14b30897a1ebbacf9934eErik String oldRrule = originalModel.mRrule; 49173714169d34a55fc01e14b30897a1ebbacf9934eErik String oldTimezone = originalModel.mTimezone; 49273714169d34a55fc01e14b30897a1ebbacf9934eErik 49373714169d34a55fc01e14b30897a1ebbacf9934eErik long newBegin = model.mStart; 49473714169d34a55fc01e14b30897a1ebbacf9934eErik long newEnd = model.mEnd; 49573714169d34a55fc01e14b30897a1ebbacf9934eErik boolean newAllDay = model.mAllDay; 49673714169d34a55fc01e14b30897a1ebbacf9934eErik String newRrule = model.mRrule; 49773714169d34a55fc01e14b30897a1ebbacf9934eErik String newTimezone = model.mTimezone; 49873714169d34a55fc01e14b30897a1ebbacf9934eErik 49973714169d34a55fc01e14b30897a1ebbacf9934eErik // If none of the time-dependent fields changed, then remove them. 50073714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldBegin == newBegin && oldEnd == newEnd && oldAllDay == newAllDay 50173714169d34a55fc01e14b30897a1ebbacf9934eErik && TextUtils.equals(oldRrule, newRrule) 502eca82e9468c002590045338f98588ad3ce1a0bb3Erik && TextUtils.equals(oldTimezone, newTimezone)) { 50373714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DTSTART); 50473714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DTEND); 50573714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DURATION); 50673714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.ALL_DAY); 50773714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.RRULE); 50873714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.EVENT_TIMEZONE); 50973714169d34a55fc01e14b30897a1ebbacf9934eErik return; 51073714169d34a55fc01e14b30897a1ebbacf9934eErik } 51173714169d34a55fc01e14b30897a1ebbacf9934eErik 51273714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldRrule == null || newRrule == null) { 51373714169d34a55fc01e14b30897a1ebbacf9934eErik return; 51473714169d34a55fc01e14b30897a1ebbacf9934eErik } 51573714169d34a55fc01e14b30897a1ebbacf9934eErik 51673714169d34a55fc01e14b30897a1ebbacf9934eErik // If we are modifying all events then we need to set DTSTART to the 51773714169d34a55fc01e14b30897a1ebbacf9934eErik // start time of the first event in the series, not the current 518eca82e9468c002590045338f98588ad3ce1a0bb3Erik // date and time. If the start time of the event was changed 51973714169d34a55fc01e14b30897a1ebbacf9934eErik // (from, say, 3pm to 4pm), then we want to add the time difference 52073714169d34a55fc01e14b30897a1ebbacf9934eErik // to the start time of the first event in the series (the DTSTART 521eca82e9468c002590045338f98588ad3ce1a0bb3Erik // value). If we are modifying one instance or all following instances, 52273714169d34a55fc01e14b30897a1ebbacf9934eErik // then we leave the DTSTART field alone. 52373714169d34a55fc01e14b30897a1ebbacf9934eErik if (modifyWhich == MODIFY_ALL) { 52473714169d34a55fc01e14b30897a1ebbacf9934eErik long oldStartMillis = originalModel.mStart; 52573714169d34a55fc01e14b30897a1ebbacf9934eErik long oldStartMillis2 = oldStartMillis; 52673714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldBegin != newBegin) { 52773714169d34a55fc01e14b30897a1ebbacf9934eErik // The user changed the start time of this event 52873714169d34a55fc01e14b30897a1ebbacf9934eErik long offset = newBegin - oldBegin; 52973714169d34a55fc01e14b30897a1ebbacf9934eErik oldStartMillis += offset; 53073714169d34a55fc01e14b30897a1ebbacf9934eErik } 53173714169d34a55fc01e14b30897a1ebbacf9934eErik if (newAllDay) { 53273714169d34a55fc01e14b30897a1ebbacf9934eErik Time time = new Time(Time.TIMEZONE_UTC); 53373714169d34a55fc01e14b30897a1ebbacf9934eErik time.set(oldStartMillis); 53473714169d34a55fc01e14b30897a1ebbacf9934eErik time.hour = 0; 53573714169d34a55fc01e14b30897a1ebbacf9934eErik time.minute = 0; 53673714169d34a55fc01e14b30897a1ebbacf9934eErik time.second = 0; 53773714169d34a55fc01e14b30897a1ebbacf9934eErik oldStartMillis = time.toMillis(false); 53873714169d34a55fc01e14b30897a1ebbacf9934eErik } 53973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTSTART, oldStartMillis); 54073714169d34a55fc01e14b30897a1ebbacf9934eErik } 54173714169d34a55fc01e14b30897a1ebbacf9934eErik } 54273714169d34a55fc01e14b30897a1ebbacf9934eErik 54373714169d34a55fc01e14b30897a1ebbacf9934eErik /** 544eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Prepares an update to the original event so it stops where the new series 545eca82e9468c002590045338f98588ad3ce1a0bb3Erik * begins When we update 'this and all following' events we need to change 546eca82e9468c002590045338f98588ad3ce1a0bb3Erik * the original event to end before a new series starts. This creates an 547eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update to the old event's rrule to do that. 54873714169d34a55fc01e14b30897a1ebbacf9934eErik * 54973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops The list of operations to add the update to 55073714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalModel The original event that we're updating 55173714169d34a55fc01e14b30897a1ebbacf9934eErik * @param initialBeginTime The original start time for the exception 55273714169d34a55fc01e14b30897a1ebbacf9934eErik */ 55373714169d34a55fc01e14b30897a1ebbacf9934eErik void updatePastEvents(ArrayList<ContentProviderOperation> ops, 55473714169d34a55fc01e14b30897a1ebbacf9934eErik CalendarEventModel originalModel, long initialBeginTime) { 55573714169d34a55fc01e14b30897a1ebbacf9934eErik boolean allDay = originalModel.mAllDay; 55673714169d34a55fc01e14b30897a1ebbacf9934eErik String oldRrule = originalModel.mRrule; 557eca82e9468c002590045338f98588ad3ce1a0bb3Erik 55873714169d34a55fc01e14b30897a1ebbacf9934eErik EventRecurrence eventRecurrence = new EventRecurrence(); 55973714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.parse(oldRrule); 56073714169d34a55fc01e14b30897a1ebbacf9934eErik 56173714169d34a55fc01e14b30897a1ebbacf9934eErik Time untilTime = new Time(); 562eca82e9468c002590045338f98588ad3ce1a0bb3Erik Time dtstart = new Time(); 56373714169d34a55fc01e14b30897a1ebbacf9934eErik long begin = initialBeginTime; 56473714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues oldValues = new ContentValues(); 56573714169d34a55fc01e14b30897a1ebbacf9934eErik 56673714169d34a55fc01e14b30897a1ebbacf9934eErik // The "until" time must be in UTC time in order for Google calendar 567eca82e9468c002590045338f98588ad3ce1a0bb3Erik // to display it properly. For all-day events, the "until" time string 568eca82e9468c002590045338f98588ad3ce1a0bb3Erik // must include just the date field, and not the time field. The 56973714169d34a55fc01e14b30897a1ebbacf9934eErik // repeating events repeat up to and including the "until" time. 57073714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.timezone = Time.TIMEZONE_UTC; 571eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.timezone = originalModel.mTimezone; 572eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.set(originalModel.mStart); 57373714169d34a55fc01e14b30897a1ebbacf9934eErik 57473714169d34a55fc01e14b30897a1ebbacf9934eErik // Subtract one second from the old begin time to get the new 57573714169d34a55fc01e14b30897a1ebbacf9934eErik // "until" time. 576eca82e9468c002590045338f98588ad3ce1a0bb3Erik untilTime.set(begin - 1000); // subtract one second (1000 millis) 57773714169d34a55fc01e14b30897a1ebbacf9934eErik if (allDay) { 57873714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.hour = 0; 57973714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.minute = 0; 58073714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.second = 0; 58173714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.allDay = true; 58273714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.normalize(false); 583eca82e9468c002590045338f98588ad3ce1a0bb3Erik 584eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.hour = 0; 585eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.minute = 0; 586eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.second = 0; 587eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.allDay = true; 588eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.timezone = Time.TIMEZONE_UTC; 58973714169d34a55fc01e14b30897a1ebbacf9934eErik } 59073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.until = untilTime.format2445(); 59173714169d34a55fc01e14b30897a1ebbacf9934eErik 59273714169d34a55fc01e14b30897a1ebbacf9934eErik oldValues.put(Events.RRULE, eventRecurrence.toString()); 593eca82e9468c002590045338f98588ad3ce1a0bb3Erik oldValues.put(Events.DTSTART, dtstart.normalize(true)); 594eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate(originalModel.mUri) 595eca82e9468c002590045338f98588ad3ce1a0bb3Erik .withValues(oldValues); 59673714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 59773714169d34a55fc01e14b30897a1ebbacf9934eErik } 59873714169d34a55fc01e14b30897a1ebbacf9934eErik 599eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Constructs a label given an arbitrary number of minutes. For example, 60073714169d34a55fc01e14b30897a1ebbacf9934eErik // if the given minutes is 63, then this returns the string "63 minutes". 60173714169d34a55fc01e14b30897a1ebbacf9934eErik // As another example, if the given minutes is 120, then this returns 60273714169d34a55fc01e14b30897a1ebbacf9934eErik // "2 hours". 60373714169d34a55fc01e14b30897a1ebbacf9934eErik String constructReminderLabel(int minutes, boolean abbrev) { 60473714169d34a55fc01e14b30897a1ebbacf9934eErik Resources resources = mActivity.getResources(); 60573714169d34a55fc01e14b30897a1ebbacf9934eErik int value, resId; 60673714169d34a55fc01e14b30897a1ebbacf9934eErik 60773714169d34a55fc01e14b30897a1ebbacf9934eErik if (minutes % 60 != 0) { 60873714169d34a55fc01e14b30897a1ebbacf9934eErik value = minutes; 60973714169d34a55fc01e14b30897a1ebbacf9934eErik if (abbrev) { 61073714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nmins; 61173714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 61273714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nminutes; 61373714169d34a55fc01e14b30897a1ebbacf9934eErik } 61473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (minutes % (24 * 60) != 0) { 61573714169d34a55fc01e14b30897a1ebbacf9934eErik value = minutes / 60; 61673714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nhours; 61773714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 618eca82e9468c002590045338f98588ad3ce1a0bb3Erik value = minutes / (24 * 60); 61973714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Ndays; 62073714169d34a55fc01e14b30897a1ebbacf9934eErik } 62173714169d34a55fc01e14b30897a1ebbacf9934eErik 62273714169d34a55fc01e14b30897a1ebbacf9934eErik String format = resources.getQuantityString(resId, value); 62373714169d34a55fc01e14b30897a1ebbacf9934eErik return String.format(format, value); 62473714169d34a55fc01e14b30897a1ebbacf9934eErik } 62573714169d34a55fc01e14b30897a1ebbacf9934eErik 62673714169d34a55fc01e14b30897a1ebbacf9934eErik /** 627eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Compares two models to ensure that they refer to the same event. This is 628eca82e9468c002590045338f98588ad3ce1a0bb3Erik * a safety check to make sure an updated event model refers to the same 629eca82e9468c002590045338f98588ad3ce1a0bb3Erik * event as the original model. If the original model is null then this is a 630eca82e9468c002590045338f98588ad3ce1a0bb3Erik * new event or we're forcing an overwrite so we return true in that case. 631eca82e9468c002590045338f98588ad3ce1a0bb3Erik * The important identifiers are the Calendar Id and the Event Id. 63273714169d34a55fc01e14b30897a1ebbacf9934eErik * 63373714169d34a55fc01e14b30897a1ebbacf9934eErik * @return 63473714169d34a55fc01e14b30897a1ebbacf9934eErik */ 63573714169d34a55fc01e14b30897a1ebbacf9934eErik static boolean isSameEvent(CalendarEventModel model, CalendarEventModel originalModel) { 63673714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel == null) { 63773714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 63873714169d34a55fc01e14b30897a1ebbacf9934eErik } 63973714169d34a55fc01e14b30897a1ebbacf9934eErik 64073714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mCalendarId != originalModel.mCalendarId) { 64173714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 64273714169d34a55fc01e14b30897a1ebbacf9934eErik } 64373714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mId != originalModel.mId) { 64473714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 64573714169d34a55fc01e14b30897a1ebbacf9934eErik } 64673714169d34a55fc01e14b30897a1ebbacf9934eErik 64773714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 64873714169d34a55fc01e14b30897a1ebbacf9934eErik } 64973714169d34a55fc01e14b30897a1ebbacf9934eErik 650eca82e9468c002590045338f98588ad3ce1a0bb3Erik /** 651eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the reminders, if they changed. Returns true if operations to 652eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update the database were added. 65373714169d34a55fc01e14b30897a1ebbacf9934eErik * 65473714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops the array of ContentProviderOperations 65573714169d34a55fc01e14b30897a1ebbacf9934eErik * @param eventId the id of the event whose reminders are being updated 65673714169d34a55fc01e14b30897a1ebbacf9934eErik * @param reminderMinutes the array of reminders set by the user 65773714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalMinutes the original array of reminders 658eca82e9468c002590045338f98588ad3ce1a0bb3Erik * @param forceSave if true, then save the reminders even if they didn't change 65973714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if operations to update the database were added 66073714169d34a55fc01e14b30897a1ebbacf9934eErik */ 6618d69cd014d912e38d2ab37764aaf4aa7700c078bErik static boolean saveReminders(ArrayList<ContentProviderOperation> ops, long eventId, 66273714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> reminderMinutes, ArrayList<Integer> originalMinutes, 66373714169d34a55fc01e14b30897a1ebbacf9934eErik boolean forceSave) { 66473714169d34a55fc01e14b30897a1ebbacf9934eErik // If the reminders have not changed, then don't update the database 66573714169d34a55fc01e14b30897a1ebbacf9934eErik if (reminderMinutes.equals(originalMinutes) && !forceSave) { 66673714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 66773714169d34a55fc01e14b30897a1ebbacf9934eErik } 66873714169d34a55fc01e14b30897a1ebbacf9934eErik 66973714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete all the existing reminders for this event 67073714169d34a55fc01e14b30897a1ebbacf9934eErik String where = Reminders.EVENT_ID + "=?"; 67173714169d34a55fc01e14b30897a1ebbacf9934eErik String[] args = new String[] {Long.toString(eventId)}; 672eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation 673eca82e9468c002590045338f98588ad3ce1a0bb3Erik .newDelete(Reminders.CONTENT_URI); 67473714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(where, args); 67573714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 67673714169d34a55fc01e14b30897a1ebbacf9934eErik 67773714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 67873714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 67973714169d34a55fc01e14b30897a1ebbacf9934eErik 68073714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new reminders, if any 68173714169d34a55fc01e14b30897a1ebbacf9934eErik for (int i = 0; i < len; i++) { 68273714169d34a55fc01e14b30897a1ebbacf9934eErik int minutes = reminderMinutes.get(i); 68373714169d34a55fc01e14b30897a1ebbacf9934eErik 68473714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 68573714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.MINUTES, minutes); 68673714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.METHOD, Reminders.METHOD_ALERT); 68773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.EVENT_ID, eventId); 68873714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values); 68973714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 69073714169d34a55fc01e14b30897a1ebbacf9934eErik } 69173714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 69273714169d34a55fc01e14b30897a1ebbacf9934eErik } 69373714169d34a55fc01e14b30897a1ebbacf9934eErik 69473714169d34a55fc01e14b30897a1ebbacf9934eErik /** 695eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the reminders, if they changed. Returns true if operations to 696eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update the database were added. Uses a reference id since an id isn't 697eca82e9468c002590045338f98588ad3ce1a0bb3Erik * created until the row is added. 69873714169d34a55fc01e14b30897a1ebbacf9934eErik * 69973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops the array of ContentProviderOperations 70073714169d34a55fc01e14b30897a1ebbacf9934eErik * @param eventId the id of the event whose reminders are being updated 70173714169d34a55fc01e14b30897a1ebbacf9934eErik * @param reminderMinutes the array of reminders set by the user 70273714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalMinutes the original array of reminders 703eca82e9468c002590045338f98588ad3ce1a0bb3Erik * @param forceSave if true, then save the reminders even if they didn't change 70473714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if operations to update the database were added 70573714169d34a55fc01e14b30897a1ebbacf9934eErik */ 706eca82e9468c002590045338f98588ad3ce1a0bb3Erik boolean saveRemindersWithBackRef(ArrayList<ContentProviderOperation> ops, int eventIdIndex, 707eca82e9468c002590045338f98588ad3ce1a0bb3Erik ArrayList<Integer> reminderMinutes, ArrayList<Integer> originalMinutes, 708eca82e9468c002590045338f98588ad3ce1a0bb3Erik boolean forceSave) { 70973714169d34a55fc01e14b30897a1ebbacf9934eErik // If the reminders have not changed, then don't update the database 71073714169d34a55fc01e14b30897a1ebbacf9934eErik if (reminderMinutes.equals(originalMinutes) && !forceSave) { 71173714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 71273714169d34a55fc01e14b30897a1ebbacf9934eErik } 71373714169d34a55fc01e14b30897a1ebbacf9934eErik 71473714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete all the existing reminders for this event 715eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation 716eca82e9468c002590045338f98588ad3ce1a0bb3Erik .newDelete(Reminders.CONTENT_URI); 71773714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(Reminders.EVENT_ID + "=?", new String[1]); 71873714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelectionBackReference(0, eventIdIndex); 71973714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 72073714169d34a55fc01e14b30897a1ebbacf9934eErik 72173714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 72273714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 72373714169d34a55fc01e14b30897a1ebbacf9934eErik 72473714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new reminders, if any 72573714169d34a55fc01e14b30897a1ebbacf9934eErik for (int i = 0; i < len; i++) { 72673714169d34a55fc01e14b30897a1ebbacf9934eErik int minutes = reminderMinutes.get(i); 72773714169d34a55fc01e14b30897a1ebbacf9934eErik 72873714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 72973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.MINUTES, minutes); 73073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.METHOD, Reminders.METHOD_ALERT); 73173714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values); 73273714169d34a55fc01e14b30897a1ebbacf9934eErik b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex); 73373714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 73473714169d34a55fc01e14b30897a1ebbacf9934eErik } 73573714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 73673714169d34a55fc01e14b30897a1ebbacf9934eErik } 73773714169d34a55fc01e14b30897a1ebbacf9934eErik 738eca82e9468c002590045338f98588ad3ce1a0bb3Erik // It's the first event in the series if the start time before being 739eca82e9468c002590045338f98588ad3ce1a0bb3Erik // modified is the same as the original event's start time 74073714169d34a55fc01e14b30897a1ebbacf9934eErik static boolean isFirstEventInSeries(CalendarEventModel model, 74173714169d34a55fc01e14b30897a1ebbacf9934eErik CalendarEventModel originalModel) { 74273714169d34a55fc01e14b30897a1ebbacf9934eErik return model.mOriginalStart == originalModel.mStart; 74373714169d34a55fc01e14b30897a1ebbacf9934eErik } 74473714169d34a55fc01e14b30897a1ebbacf9934eErik 74573714169d34a55fc01e14b30897a1ebbacf9934eErik // Adds an rRule and duration to a set of content values 74673714169d34a55fc01e14b30897a1ebbacf9934eErik void addRecurrenceRule(ContentValues values, CalendarEventModel model) { 74773714169d34a55fc01e14b30897a1ebbacf9934eErik String rrule = model.mRrule; 74873714169d34a55fc01e14b30897a1ebbacf9934eErik 74973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.RRULE, rrule); 75073714169d34a55fc01e14b30897a1ebbacf9934eErik long end = model.mEnd; 75173714169d34a55fc01e14b30897a1ebbacf9934eErik long start = model.mStart; 75273714169d34a55fc01e14b30897a1ebbacf9934eErik String duration = model.mDuration; 75373714169d34a55fc01e14b30897a1ebbacf9934eErik 75437960c091b5912dd45beb561a85a9064ea42e616Erik boolean isAllDay = model.mAllDay; 75537960c091b5912dd45beb561a85a9064ea42e616Erik if (end > start) { 75637960c091b5912dd45beb561a85a9064ea42e616Erik if (isAllDay) { 75737960c091b5912dd45beb561a85a9064ea42e616Erik // if it's all day compute the duration in days 75837960c091b5912dd45beb561a85a9064ea42e616Erik long days = (end - start + DateUtils.DAY_IN_MILLIS - 1) 75937960c091b5912dd45beb561a85a9064ea42e616Erik / DateUtils.DAY_IN_MILLIS; 76037960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P" + days + "D"; 76173714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 76237960c091b5912dd45beb561a85a9064ea42e616Erik // otherwise compute the duration in seconds 76337960c091b5912dd45beb561a85a9064ea42e616Erik long seconds = (end - start) / DateUtils.SECOND_IN_MILLIS; 76437960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P" + seconds + "S"; 76537960c091b5912dd45beb561a85a9064ea42e616Erik } 76637960c091b5912dd45beb561a85a9064ea42e616Erik } else if (TextUtils.isEmpty(duration)) { 76737960c091b5912dd45beb561a85a9064ea42e616Erik 76837960c091b5912dd45beb561a85a9064ea42e616Erik // If no good duration info exists assume the default 76937960c091b5912dd45beb561a85a9064ea42e616Erik if (isAllDay) { 77037960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P1D"; 77137960c091b5912dd45beb561a85a9064ea42e616Erik } else { 77237960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P3600S"; 77373714169d34a55fc01e14b30897a1ebbacf9934eErik } 77473714169d34a55fc01e14b30897a1ebbacf9934eErik } 77573714169d34a55fc01e14b30897a1ebbacf9934eErik // recurring events should have a duration and dtend set to null 77673714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DURATION, duration); 777eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Events.DTEND, (Long) null); 77873714169d34a55fc01e14b30897a1ebbacf9934eErik } 77973714169d34a55fc01e14b30897a1ebbacf9934eErik 78073714169d34a55fc01e14b30897a1ebbacf9934eErik /** 781eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Uses the recurrence selection and the model data to build an rrule and 782eca82e9468c002590045338f98588ad3ce1a0bb3Erik * write it to the model. 78373714169d34a55fc01e14b30897a1ebbacf9934eErik * 78473714169d34a55fc01e14b30897a1ebbacf9934eErik * @param selection the type of rrule 78573714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The event to update 78673714169d34a55fc01e14b30897a1ebbacf9934eErik */ 78773714169d34a55fc01e14b30897a1ebbacf9934eErik static void updateRecurrenceRule(int selection, CalendarEventModel model) { 78873714169d34a55fc01e14b30897a1ebbacf9934eErik // Make sure we don't have any leftover data from the previous setting 78973714169d34a55fc01e14b30897a1ebbacf9934eErik EventRecurrence eventRecurrence = new EventRecurrence(); 79073714169d34a55fc01e14b30897a1ebbacf9934eErik 79173714169d34a55fc01e14b30897a1ebbacf9934eErik if (selection == DOES_NOT_REPEAT) { 79273714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = null; 79373714169d34a55fc01e14b30897a1ebbacf9934eErik return; 79473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_CUSTOM) { 79573714169d34a55fc01e14b30897a1ebbacf9934eErik // Keep custom recurrence as before. 79673714169d34a55fc01e14b30897a1ebbacf9934eErik return; 79773714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_DAILY) { 79873714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.DAILY; 79973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_EVERY_WEEKDAY) { 80073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.WEEKLY; 80173714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 5; 80273714169d34a55fc01e14b30897a1ebbacf9934eErik int[] byday = new int[dayCount]; 80373714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bydayNum = new int[dayCount]; 80473714169d34a55fc01e14b30897a1ebbacf9934eErik 80573714169d34a55fc01e14b30897a1ebbacf9934eErik byday[0] = EventRecurrence.MO; 80673714169d34a55fc01e14b30897a1ebbacf9934eErik byday[1] = EventRecurrence.TU; 80773714169d34a55fc01e14b30897a1ebbacf9934eErik byday[2] = EventRecurrence.WE; 80873714169d34a55fc01e14b30897a1ebbacf9934eErik byday[3] = EventRecurrence.TH; 80973714169d34a55fc01e14b30897a1ebbacf9934eErik byday[4] = EventRecurrence.FR; 81073714169d34a55fc01e14b30897a1ebbacf9934eErik for (int day = 0; day < dayCount; day++) { 81173714169d34a55fc01e14b30897a1ebbacf9934eErik bydayNum[day] = 0; 81273714169d34a55fc01e14b30897a1ebbacf9934eErik } 81373714169d34a55fc01e14b30897a1ebbacf9934eErik 81473714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = byday; 81573714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = bydayNum; 81673714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = dayCount; 81773714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_WEEKLY_ON_DAY) { 81873714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.WEEKLY; 81973714169d34a55fc01e14b30897a1ebbacf9934eErik int[] days = new int[1]; 82073714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 1; 82173714169d34a55fc01e14b30897a1ebbacf9934eErik int[] dayNum = new int[dayCount]; 82273714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 82373714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 82473714169d34a55fc01e14b30897a1ebbacf9934eErik 82573714169d34a55fc01e14b30897a1ebbacf9934eErik days[0] = EventRecurrence.timeDay2Day(startTime.weekDay); 82673714169d34a55fc01e14b30897a1ebbacf9934eErik // not sure why this needs to be zero, but set it for now. 82773714169d34a55fc01e14b30897a1ebbacf9934eErik dayNum[0] = 0; 82873714169d34a55fc01e14b30897a1ebbacf9934eErik 82973714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = days; 83073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = dayNum; 83173714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = dayCount; 83273714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_MONTHLY_ON_DAY) { 83373714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.MONTHLY; 83473714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = 0; 83573714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthdayCount = 1; 83673714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bymonthday = new int[1]; 83773714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 83873714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 83973714169d34a55fc01e14b30897a1ebbacf9934eErik bymonthday[0] = startTime.monthDay; 84073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthday = bymonthday; 84173714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_MONTHLY_ON_DAY_COUNT) { 84273714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.MONTHLY; 84373714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = 1; 84473714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthdayCount = 0; 84573714169d34a55fc01e14b30897a1ebbacf9934eErik 84673714169d34a55fc01e14b30897a1ebbacf9934eErik int[] byday = new int[1]; 84773714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bydayNum = new int[1]; 84873714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 84973714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 85073714169d34a55fc01e14b30897a1ebbacf9934eErik // Compute the week number (for example, the "2nd" Monday) 85173714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 1 + ((startTime.monthDay - 1) / 7); 85273714169d34a55fc01e14b30897a1ebbacf9934eErik if (dayCount == 5) { 85373714169d34a55fc01e14b30897a1ebbacf9934eErik dayCount = -1; 85473714169d34a55fc01e14b30897a1ebbacf9934eErik } 85573714169d34a55fc01e14b30897a1ebbacf9934eErik bydayNum[0] = dayCount; 85673714169d34a55fc01e14b30897a1ebbacf9934eErik byday[0] = EventRecurrence.timeDay2Day(startTime.weekDay); 85773714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = byday; 85873714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = bydayNum; 85973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_YEARLY) { 86073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.YEARLY; 86173714169d34a55fc01e14b30897a1ebbacf9934eErik } 86273714169d34a55fc01e14b30897a1ebbacf9934eErik 86373714169d34a55fc01e14b30897a1ebbacf9934eErik // Set the week start day. 864eca82e9468c002590045338f98588ad3ce1a0bb3Erik eventRecurrence.wkst = EventRecurrence.calendarDay2Day(Calendar.getInstance() 865eca82e9468c002590045338f98588ad3ce1a0bb3Erik .getFirstDayOfWeek()); 86673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = eventRecurrence.toString(); 86773714169d34a55fc01e14b30897a1ebbacf9934eErik } 86873714169d34a55fc01e14b30897a1ebbacf9934eErik 86973714169d34a55fc01e14b30897a1ebbacf9934eErik /** 870eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Uses an event cursor to fill in the given model This method assumes the 871eca82e9468c002590045338f98588ad3ce1a0bb3Erik * cursor used {@link #EVENT_PROJECTION} as it's query projection. It uses 872eca82e9468c002590045338f98588ad3ce1a0bb3Erik * the cursor to fill in the given model with all the information available. 87373714169d34a55fc01e14b30897a1ebbacf9934eErik * 87473714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The model to fill in 87573714169d34a55fc01e14b30897a1ebbacf9934eErik * @param cursor An event cursor that used {@link #EVENT_PROJECTION} for the query 87673714169d34a55fc01e14b30897a1ebbacf9934eErik */ 87788c483a0189cbe7132330cb1b563f07326f3bcf9Erik public static void setModelFromCursor(CalendarEventModel model, Cursor cursor) { 87873714169d34a55fc01e14b30897a1ebbacf9934eErik if (model == null || cursor == null || cursor.getCount() != 1) { 87973714169d34a55fc01e14b30897a1ebbacf9934eErik Log.wtf(TAG, "Attempted to build non-existent model or from an incorrect query."); 88073714169d34a55fc01e14b30897a1ebbacf9934eErik return; 88173714169d34a55fc01e14b30897a1ebbacf9934eErik } 88273714169d34a55fc01e14b30897a1ebbacf9934eErik 88373714169d34a55fc01e14b30897a1ebbacf9934eErik model.clear(); 88473714169d34a55fc01e14b30897a1ebbacf9934eErik cursor.moveToFirst(); 88573714169d34a55fc01e14b30897a1ebbacf9934eErik 88673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mId = cursor.getInt(EVENT_INDEX_ID); 88773714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTitle = cursor.getString(EVENT_INDEX_TITLE); 88873714169d34a55fc01e14b30897a1ebbacf9934eErik model.mDescription = cursor.getString(EVENT_INDEX_DESCRIPTION); 88973714169d34a55fc01e14b30897a1ebbacf9934eErik model.mLocation = cursor.getString(EVENT_INDEX_EVENT_LOCATION); 89073714169d34a55fc01e14b30897a1ebbacf9934eErik model.mAllDay = cursor.getInt(EVENT_INDEX_ALL_DAY) != 0; 89173714169d34a55fc01e14b30897a1ebbacf9934eErik model.mHasAlarm = cursor.getInt(EVENT_INDEX_HAS_ALARM) != 0; 89273714169d34a55fc01e14b30897a1ebbacf9934eErik model.mCalendarId = cursor.getInt(EVENT_INDEX_CALENDAR_ID); 89373714169d34a55fc01e14b30897a1ebbacf9934eErik model.mStart = cursor.getLong(EVENT_INDEX_DTSTART); 89473714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTimezone = cursor.getString(EVENT_INDEX_TIMEZONE); 89573714169d34a55fc01e14b30897a1ebbacf9934eErik String rRule = cursor.getString(EVENT_INDEX_RRULE); 89673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = rRule; 89773714169d34a55fc01e14b30897a1ebbacf9934eErik model.mSyncId = cursor.getString(EVENT_INDEX_SYNC_ID); 89873714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTransparency = cursor.getInt(EVENT_INDEX_TRANSPARENCY) != 0; 89973714169d34a55fc01e14b30897a1ebbacf9934eErik int visibility = cursor.getInt(EVENT_INDEX_VISIBILITY); 90073714169d34a55fc01e14b30897a1ebbacf9934eErik model.mOwnerAccount = cursor.getString(EVENT_INDEX_OWNER_ACCOUNT); 90173714169d34a55fc01e14b30897a1ebbacf9934eErik model.mHasAttendeeData = cursor.getInt(EVENT_INDEX_HAS_ATTENDEE_DATA) != 0; 902eca82e9468c002590045338f98588ad3ce1a0bb3Erik model.mOriginalEvent = cursor.getString(EVENT_INDEX_ORIGINAL_EVENT); 90373714169d34a55fc01e14b30897a1ebbacf9934eErik 90473714169d34a55fc01e14b30897a1ebbacf9934eErik if (visibility > 0) { 905eca82e9468c002590045338f98588ad3ce1a0bb3Erik // For now the array contains the values 0, 2, and 3. We subtract 906eca82e9468c002590045338f98588ad3ce1a0bb3Erik // one to make it easier to handle in code as 0,1,2. 90773714169d34a55fc01e14b30897a1ebbacf9934eErik // Default (0), Private (1), Public (2) 90873714169d34a55fc01e14b30897a1ebbacf9934eErik visibility--; 90973714169d34a55fc01e14b30897a1ebbacf9934eErik } 91073714169d34a55fc01e14b30897a1ebbacf9934eErik model.mVisibility = visibility; 91173714169d34a55fc01e14b30897a1ebbacf9934eErik 91273714169d34a55fc01e14b30897a1ebbacf9934eErik boolean hasRRule = !TextUtils.isEmpty(rRule); 91373714169d34a55fc01e14b30897a1ebbacf9934eErik 91473714169d34a55fc01e14b30897a1ebbacf9934eErik // We expect only one of these, so ignore the other 91573714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasRRule) { 91673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mDuration = cursor.getString(EVENT_INDEX_DURATION); 91773714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 91873714169d34a55fc01e14b30897a1ebbacf9934eErik model.mEnd = cursor.getLong(EVENT_INDEX_DTEND); 91973714169d34a55fc01e14b30897a1ebbacf9934eErik } 92073714169d34a55fc01e14b30897a1ebbacf9934eErik } 92173714169d34a55fc01e14b30897a1ebbacf9934eErik 92273714169d34a55fc01e14b30897a1ebbacf9934eErik /** 923eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Goes through an event model and fills in content values for saving This 924eca82e9468c002590045338f98588ad3ce1a0bb3Erik * method will perform the initial collection of values from the model and 925eca82e9468c002590045338f98588ad3ce1a0bb3Erik * put them into a set of ContentValues. It performs some basic work such as 926eca82e9468c002590045338f98588ad3ce1a0bb3Erik * fixing the time on allDay events and choosing whether to use an rrule or 927eca82e9468c002590045338f98588ad3ce1a0bb3Erik * dtend. 92873714169d34a55fc01e14b30897a1ebbacf9934eErik * 92973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The complete model of the event you want to save 93073714169d34a55fc01e14b30897a1ebbacf9934eErik * @return values 93173714169d34a55fc01e14b30897a1ebbacf9934eErik */ 93273714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues getContentValuesFromModel(CalendarEventModel model) { 93373714169d34a55fc01e14b30897a1ebbacf9934eErik String title = model.mTitle; 93473714169d34a55fc01e14b30897a1ebbacf9934eErik boolean isAllDay = model.mAllDay; 93573714169d34a55fc01e14b30897a1ebbacf9934eErik String location = model.mLocation.trim(); 93673714169d34a55fc01e14b30897a1ebbacf9934eErik String description = model.mDescription.trim(); 93773714169d34a55fc01e14b30897a1ebbacf9934eErik String rrule = model.mRrule; 938eca82e9468c002590045338f98588ad3ce1a0bb3Erik String timezone = model.mTimezone; 93973714169d34a55fc01e14b30897a1ebbacf9934eErik if (timezone == null) { 94073714169d34a55fc01e14b30897a1ebbacf9934eErik timezone = TimeZone.getDefault().getID(); 94173714169d34a55fc01e14b30897a1ebbacf9934eErik } 94273714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(timezone); 94373714169d34a55fc01e14b30897a1ebbacf9934eErik Time endTime = new Time(timezone); 94473714169d34a55fc01e14b30897a1ebbacf9934eErik 94573714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 94673714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.set(model.mEnd); 94773714169d34a55fc01e14b30897a1ebbacf9934eErik 94873714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 94973714169d34a55fc01e14b30897a1ebbacf9934eErik 95073714169d34a55fc01e14b30897a1ebbacf9934eErik long startMillis; 95173714169d34a55fc01e14b30897a1ebbacf9934eErik long endMillis; 95273714169d34a55fc01e14b30897a1ebbacf9934eErik long calendarId = model.mCalendarId; 95373714169d34a55fc01e14b30897a1ebbacf9934eErik if (isAllDay) { 95473714169d34a55fc01e14b30897a1ebbacf9934eErik // Reset start and end time, ensure at least 1 day duration, and set 95573714169d34a55fc01e14b30897a1ebbacf9934eErik // the timezone to UTC, as required for all-day events. 95673714169d34a55fc01e14b30897a1ebbacf9934eErik timezone = Time.TIMEZONE_UTC; 95773714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.hour = 0; 95873714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.minute = 0; 95973714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.second = 0; 96073714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.timezone = timezone; 96173714169d34a55fc01e14b30897a1ebbacf9934eErik startMillis = startTime.normalize(true); 96273714169d34a55fc01e14b30897a1ebbacf9934eErik 96373714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.hour = 0; 96473714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.minute = 0; 96573714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.second = 0; 96673714169d34a55fc01e14b30897a1ebbacf9934eErik if (endTime.monthDay == startTime.monthDay) { 96773714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.monthDay++; 96873714169d34a55fc01e14b30897a1ebbacf9934eErik } 96973714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.timezone = timezone; 97073714169d34a55fc01e14b30897a1ebbacf9934eErik endMillis = endTime.normalize(true); 97173714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 97273714169d34a55fc01e14b30897a1ebbacf9934eErik startMillis = startTime.toMillis(true); 97373714169d34a55fc01e14b30897a1ebbacf9934eErik endMillis = endTime.toMillis(true); 97473714169d34a55fc01e14b30897a1ebbacf9934eErik } 97573714169d34a55fc01e14b30897a1ebbacf9934eErik 97673714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.CALENDAR_ID, calendarId); 97773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.EVENT_TIMEZONE, timezone); 97873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.TITLE, title); 97973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ALL_DAY, isAllDay ? 1 : 0); 98073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTSTART, startMillis); 98173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.RRULE, rrule); 98273714169d34a55fc01e14b30897a1ebbacf9934eErik if (rrule != null) { 98373714169d34a55fc01e14b30897a1ebbacf9934eErik addRecurrenceRule(values, model); 98473714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 985eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Events.DURATION, (String) null); 98673714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTEND, endMillis); 98773714169d34a55fc01e14b30897a1ebbacf9934eErik } 98873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DESCRIPTION, description); 98973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.EVENT_LOCATION, location); 99073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.TRANSPARENCY, model.mTransparency ? 1 : 0); 99188c483a0189cbe7132330cb1b563f07326f3bcf9Erik values.put(Events.HAS_ATTENDEE_DATA, model.mHasAttendeeData ? 1 : 0); 99273714169d34a55fc01e14b30897a1ebbacf9934eErik 99373714169d34a55fc01e14b30897a1ebbacf9934eErik int visibility = model.mVisibility; 99473714169d34a55fc01e14b30897a1ebbacf9934eErik if (visibility > 0) { 99573714169d34a55fc01e14b30897a1ebbacf9934eErik // For now the array contains the values 0, 2, and 3. We add one to match. 99673714169d34a55fc01e14b30897a1ebbacf9934eErik // Default (0), Private (2), Public (3) 99773714169d34a55fc01e14b30897a1ebbacf9934eErik visibility++; 99873714169d34a55fc01e14b30897a1ebbacf9934eErik } 99973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.VISIBILITY, visibility); 100073714169d34a55fc01e14b30897a1ebbacf9934eErik 100173714169d34a55fc01e14b30897a1ebbacf9934eErik return values; 100273714169d34a55fc01e14b30897a1ebbacf9934eErik } 100373714169d34a55fc01e14b30897a1ebbacf9934eErik 100473714169d34a55fc01e14b30897a1ebbacf9934eErik /** 100573714169d34a55fc01e14b30897a1ebbacf9934eErik * Takes an e-mail address and returns the domain (everything after the last @) 100673714169d34a55fc01e14b30897a1ebbacf9934eErik */ 100773714169d34a55fc01e14b30897a1ebbacf9934eErik public static String extractDomain(String email) { 100873714169d34a55fc01e14b30897a1ebbacf9934eErik int separator = email.lastIndexOf('@'); 100973714169d34a55fc01e14b30897a1ebbacf9934eErik if (separator != -1 && ++separator < email.length()) { 101073714169d34a55fc01e14b30897a1ebbacf9934eErik return email.substring(separator); 101173714169d34a55fc01e14b30897a1ebbacf9934eErik } 101273714169d34a55fc01e14b30897a1ebbacf9934eErik return null; 101373714169d34a55fc01e14b30897a1ebbacf9934eErik } 101479f228124de7d98146ca526d743436f6419e2365Erik 101579f228124de7d98146ca526d743436f6419e2365Erik public interface EditDoneRunnable extends Runnable { 101679f228124de7d98146ca526d743436f6419e2365Erik public void setDoneCode(int code); 101779f228124de7d98146ca526d743436f6419e2365Erik } 101873714169d34a55fc01e14b30897a1ebbacf9934eErik} 1019