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