EditEventHelper.java revision deced8999da73effe4133e2317c262fea7910090
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 17fbce65e53c7a111955f638db5bf8bee35381e5b7Erikpackage com.android.calendar.event; 1873714169d34a55fc01e14b30897a1ebbacf9934eErik 19fbce65e53c7a111955f638db5bf8bee35381e5b7Erikimport com.android.calendar.AsyncQueryService; 20deced8999da73effe4133e2317c262fea7910090Michael Chanimport com.android.calendar.CalendarController; 21fbce65e53c7a111955f638db5bf8bee35381e5b7Erikimport com.android.calendar.CalendarEventModel; 22d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport com.android.calendar.CalendarEventModel.Attendee; 23fbce65e53c7a111955f638db5bf8bee35381e5b7Erikimport com.android.calendar.R; 24fbce65e53c7a111955f638db5bf8bee35381e5b7Erikimport com.android.calendar.Utils; 2573714169d34a55fc01e14b30897a1ebbacf9934eErikimport com.android.common.Rfc822Validator; 2673714169d34a55fc01e14b30897a1ebbacf9934eErik 27eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentProviderOperation; 28eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentUris; 29eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.content.ContentValues; 30dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.content.Context; 3173714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.content.res.Resources; 3273714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.database.Cursor; 33d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport android.graphics.drawable.Drawable; 3473714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.net.Uri; 3573714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.pim.EventRecurrence; 3673714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Attendees; 37eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport android.provider.Calendar.Calendars; 3873714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Events; 3973714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.provider.Calendar.Reminders; 4073714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.TextUtils; 4173714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.format.DateUtils; 4273714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.format.Time; 4373714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.util.Rfc822Token; 4473714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.text.util.Rfc822Tokenizer; 4573714169d34a55fc01e14b30897a1ebbacf9934eErikimport android.util.Log; 46352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chanimport android.view.View; 47d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport android.widget.ImageView; 48d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport android.widget.QuickContactBadge; 4973714169d34a55fc01e14b30897a1ebbacf9934eErik 50eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.ArrayList; 51d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport java.util.HashMap; 52eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.Iterator; 53eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.LinkedHashSet; 54d845fbe558d5229102b58cce70a1b29fe6cb6967Erikimport java.util.LinkedList; 55eca82e9468c002590045338f98588ad3ce1a0bb3Erikimport java.util.TimeZone; 5673714169d34a55fc01e14b30897a1ebbacf9934eErik 5773714169d34a55fc01e14b30897a1ebbacf9934eErikpublic class EditEventHelper { 5873714169d34a55fc01e14b30897a1ebbacf9934eErik private static final String TAG = "EditEventHelper"; 59eca82e9468c002590045338f98588ad3ce1a0bb3Erik 6073714169d34a55fc01e14b30897a1ebbacf9934eErik private static final boolean DEBUG = false; 6173714169d34a55fc01e14b30897a1ebbacf9934eErik 62eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int MAX_REMINDERS = 5; 63eca82e9468c002590045338f98588ad3ce1a0bb3Erik 64fbce65e53c7a111955f638db5bf8bee35381e5b7Erik public static final String[] EVENT_PROJECTION = new String[] { 6573714169d34a55fc01e14b30897a1ebbacf9934eErik Events._ID, // 0 6673714169d34a55fc01e14b30897a1ebbacf9934eErik Events.TITLE, // 1 6773714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DESCRIPTION, // 2 6873714169d34a55fc01e14b30897a1ebbacf9934eErik Events.EVENT_LOCATION, // 3 6973714169d34a55fc01e14b30897a1ebbacf9934eErik Events.ALL_DAY, // 4 7073714169d34a55fc01e14b30897a1ebbacf9934eErik Events.HAS_ALARM, // 5 7173714169d34a55fc01e14b30897a1ebbacf9934eErik Events.CALENDAR_ID, // 6 7273714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DTSTART, // 7 7373714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DTEND, // 8 7473714169d34a55fc01e14b30897a1ebbacf9934eErik Events.DURATION, // 9 7573714169d34a55fc01e14b30897a1ebbacf9934eErik Events.EVENT_TIMEZONE, // 10 7673714169d34a55fc01e14b30897a1ebbacf9934eErik Events.RRULE, // 11 7773714169d34a55fc01e14b30897a1ebbacf9934eErik Events._SYNC_ID, // 12 7873714169d34a55fc01e14b30897a1ebbacf9934eErik Events.TRANSPARENCY, // 13 7973714169d34a55fc01e14b30897a1ebbacf9934eErik Events.VISIBILITY, // 14 8073714169d34a55fc01e14b30897a1ebbacf9934eErik Events.OWNER_ACCOUNT, // 15 8173714169d34a55fc01e14b30897a1ebbacf9934eErik Events.HAS_ATTENDEE_DATA, // 16 82eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.ORIGINAL_EVENT, // 17 83352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Events.ORGANIZER, // 18 84352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Events.GUESTS_CAN_MODIFY, // 19 8573714169d34a55fc01e14b30897a1ebbacf9934eErik }; 8673714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_ID = 0; 8773714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TITLE = 1; 8873714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DESCRIPTION = 2; 8973714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_EVENT_LOCATION = 3; 9073714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_ALL_DAY = 4; 9173714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_HAS_ALARM = 5; 9273714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_CALENDAR_ID = 6; 9373714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DTSTART = 7; 9473714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DTEND = 8; 9573714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_DURATION = 9; 9673714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TIMEZONE = 10; 9773714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_RRULE = 11; 9873714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_SYNC_ID = 12; 9973714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_TRANSPARENCY = 13; 10073714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_VISIBILITY = 14; 10173714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_OWNER_ACCOUNT = 15; 10273714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 16; 103eca82e9468c002590045338f98588ad3ce1a0bb3Erik protected static final int EVENT_INDEX_ORIGINAL_EVENT = 17; 104352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan protected static final int EVENT_INDEX_ORGANIZER = 18; 105352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan protected static final int EVENT_INDEX_GUESTS_CAN_MODIFY = 19; 10673714169d34a55fc01e14b30897a1ebbacf9934eErik 107eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String[] REMINDERS_PROJECTION = new String[] { 10873714169d34a55fc01e14b30897a1ebbacf9934eErik Reminders._ID, // 0 10973714169d34a55fc01e14b30897a1ebbacf9934eErik Reminders.MINUTES, // 1 110eca82e9468c002590045338f98588ad3ce1a0bb3Erik Reminders.METHOD, // 2 11173714169d34a55fc01e14b30897a1ebbacf9934eErik }; 112eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int REMINDERS_INDEX_MINUTES = 1; 113eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final int REMINDERS_INDEX_METHOD = 2; 114eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=? AND (" + Reminders.METHOD 115eca82e9468c002590045338f98588ad3ce1a0bb3Erik + "=?" + " OR " + Reminders.METHOD + "=?" + ")"; 116eca82e9468c002590045338f98588ad3ce1a0bb3Erik 11773714169d34a55fc01e14b30897a1ebbacf9934eErik // Visible for testing 118eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String ATTENDEES_DELETE_PREFIX = Attendees.EVENT_ID + "=? AND " 119eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Attendees.ATTENDEE_EMAIL + " IN ("; 12073714169d34a55fc01e14b30897a1ebbacf9934eErik 12173714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int DOES_NOT_REPEAT = 0; 12273714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_DAILY = 1; 12373714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_EVERY_WEEKDAY = 2; 12473714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_WEEKLY_ON_DAY = 3; 12573714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_MONTHLY_ON_DAY_COUNT = 4; 12673714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_MONTHLY_ON_DAY = 5; 12773714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_YEARLY = 6; 12873714169d34a55fc01e14b30897a1ebbacf9934eErik public static final int REPEATS_CUSTOM = 7; 12973714169d34a55fc01e14b30897a1ebbacf9934eErik 13073714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int MODIFY_UNINITIALIZED = 0; 13173714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int MODIFY_SELECTED = 1; 1328d69cd014d912e38d2ab37764aaf4aa7700c078bErik protected static final int MODIFY_ALL_FOLLOWING = 2; 1338d69cd014d912e38d2ab37764aaf4aa7700c078bErik protected static final int MODIFY_ALL = 3; 13473714169d34a55fc01e14b30897a1ebbacf9934eErik 13573714169d34a55fc01e14b30897a1ebbacf9934eErik protected static final int DAY_IN_SECONDS = 24 * 60 * 60; 13673714169d34a55fc01e14b30897a1ebbacf9934eErik 137dd95df57c8c5a58a85c4c0effad5652dec14f621Erik private Context mContext; 13837960c091b5912dd45beb561a85a9064ea42e616Erik private AsyncQueryService mService; 139eca82e9468c002590045338f98588ad3ce1a0bb3Erik 140eca82e9468c002590045338f98588ad3ce1a0bb3Erik // public int mModification; 14173714169d34a55fc01e14b30897a1ebbacf9934eErik private Rfc822Validator mEmailValidator; 142eca82e9468c002590045338f98588ad3ce1a0bb3Erik 143eca82e9468c002590045338f98588ad3ce1a0bb3Erik // This allows us to flag the event if something is wrong with it, right now 144eca82e9468c002590045338f98588ad3ce1a0bb3Erik // if an uri is provided for an event that doesn't exist in the db. 14573714169d34a55fc01e14b30897a1ebbacf9934eErik protected boolean mEventOk = true; 14673714169d34a55fc01e14b30897a1ebbacf9934eErik 147d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public static final int ATTENDEE_ID_NONE = -1; 148d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public static final int[] ATTENDEE_VALUES = { 149deced8999da73effe4133e2317c262fea7910090Michael Chan CalendarController.ATTENDEE_NO_RESPONSE, 150d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_STATUS_ACCEPTED, 151d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_STATUS_TENTATIVE, 152d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_STATUS_DECLINED, 153d845fbe558d5229102b58cce70a1b29fe6cb6967Erik }; 154d845fbe558d5229102b58cce70a1b29fe6cb6967Erik 155eca82e9468c002590045338f98588ad3ce1a0bb3Erik /** 156eca82e9468c002590045338f98588ad3ce1a0bb3Erik * This is the symbolic name for the key used to pass in the boolean for 157eca82e9468c002590045338f98588ad3ce1a0bb3Erik * creating all-day events that is part of the extra data of the intent. 158eca82e9468c002590045338f98588ad3ce1a0bb3Erik * This is used only for creating new events and is set to true if the 159eca82e9468c002590045338f98588ad3ce1a0bb3Erik * default for the new event should be an all-day event. 160eca82e9468c002590045338f98588ad3ce1a0bb3Erik */ 161eca82e9468c002590045338f98588ad3ce1a0bb3Erik public static final String EVENT_ALL_DAY = "allDay"; 162eca82e9468c002590045338f98588ad3ce1a0bb3Erik 163eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String[] CALENDARS_PROJECTION = new String[] { 164eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars._ID, // 0 165eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.DISPLAY_NAME, // 1 166eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.OWNER_ACCOUNT, // 2 167eca82e9468c002590045338f98588ad3ce1a0bb3Erik Calendars.COLOR, // 3 168352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Calendars.ORGANIZER_CAN_RESPOND, // 4 169352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Calendars.ACCESS_LEVEL, // 5 170352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Calendars.SELECTED, // 6 171eca82e9468c002590045338f98588ad3ce1a0bb3Erik }; 172eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_ID = 0; 173eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_DISPLAY_NAME = 1; 174eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; 175eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final int CALENDARS_INDEX_COLOR = 3; 176352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan static final int CALENDARS_INDEX_ORGANIZER_CAN_RESPOND = 4; 177352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan static final int CALENDARS_INDEX_ACCESS_LEVEL = 5; 178352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan static final int CALENDARS_INDEX_SELECTED = 6; 179eca82e9468c002590045338f98588ad3ce1a0bb3Erik 180eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String CALENDARS_WHERE_WRITEABLE_VISIBLE = Calendars.ACCESS_LEVEL + ">=" 181eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Calendars.CONTRIBUTOR_ACCESS + " AND " + Calendars.SELECTED + "=1"; 182eca82e9468c002590045338f98588ad3ce1a0bb3Erik 183352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan static final String CALENDARS_WHERE = Calendars._ID + "=?"; 184352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 185eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String[] ATTENDEES_PROJECTION = new String[] { 186d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees._ID, // 0 187d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_NAME, // 1 188d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_EMAIL, // 2 189d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_RELATIONSHIP, // 3 190d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Attendees.ATTENDEE_STATUS, // 4 191eca82e9468c002590045338f98588ad3ce1a0bb3Erik }; 192d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final int ATTENDEES_INDEX_ID = 0; 193d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final int ATTENDEES_INDEX_NAME = 1; 194d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final int ATTENDEES_INDEX_EMAIL = 2; 195d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final int ATTENDEES_INDEX_RELATIONSHIP = 3; 196d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final int ATTENDEES_INDEX_STATUS = 4; 197eca82e9468c002590045338f98588ad3ce1a0bb3Erik static final String ATTENDEES_WHERE_NOT_ORGANIZER = Attendees.EVENT_ID + "=? AND " 198eca82e9468c002590045338f98588ad3ce1a0bb3Erik + Attendees.ATTENDEE_RELATIONSHIP + "<>" + Attendees.RELATIONSHIP_ORGANIZER; 199d845fbe558d5229102b58cce70a1b29fe6cb6967Erik static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; 200d845fbe558d5229102b58cce70a1b29fe6cb6967Erik 201d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public static class ContactViewHolder { 202d845fbe558d5229102b58cce70a1b29fe6cb6967Erik QuickContactBadge badge; 203d845fbe558d5229102b58cce70a1b29fe6cb6967Erik ImageView presence; 204d845fbe558d5229102b58cce70a1b29fe6cb6967Erik int updateCounts; 205d845fbe558d5229102b58cce70a1b29fe6cb6967Erik } 206d845fbe558d5229102b58cce70a1b29fe6cb6967Erik 207d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public static class AttendeeItem { 208d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public boolean mRemoved; 209d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public Attendee mAttendee; 210d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public Drawable mBadge; 211d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public int mPresence; 212d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public int mUpdateCounts; 213bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa public View mView; 214bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa 215bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa public AttendeeItem(Attendee attendee, int presence, Drawable badge) { 216bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa mAttendee = attendee; 217bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa mPresence = presence; 218bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa mBadge = badge; 219bdcb9fcc732d52708fafb068f535a2c93ff2356fDaisuke Miyakawa } 220d845fbe558d5229102b58cce70a1b29fe6cb6967Erik } 221eca82e9468c002590045338f98588ad3ce1a0bb3Erik 222dd95df57c8c5a58a85c4c0effad5652dec14f621Erik public EditEventHelper(Context context, CalendarEventModel model) { 223dd95df57c8c5a58a85c4c0effad5652dec14f621Erik mContext = context; 224dd95df57c8c5a58a85c4c0effad5652dec14f621Erik mService = new AsyncQueryService(context); 2256ae7d69b90f22fd51ab677036ba987dbdc35a62dErik setDomainFromModel(model); 2266ae7d69b90f22fd51ab677036ba987dbdc35a62dErik } 2276ae7d69b90f22fd51ab677036ba987dbdc35a62dErik 228eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Sets up the email validator for the given model 2296ae7d69b90f22fd51ab677036ba987dbdc35a62dErik public void setDomainFromModel(CalendarEventModel model) { 23073714169d34a55fc01e14b30897a1ebbacf9934eErik String domain = "gmail.com"; 231eca82e9468c002590045338f98588ad3ce1a0bb3Erik if (model != null) { 23273714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerAccount = model.mOwnerAccount; 23373714169d34a55fc01e14b30897a1ebbacf9934eErik if (!TextUtils.isEmpty(ownerAccount)) { 23473714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerDomain = extractDomain(ownerAccount); 23573714169d34a55fc01e14b30897a1ebbacf9934eErik if (!TextUtils.isEmpty(ownerDomain)) { 23673714169d34a55fc01e14b30897a1ebbacf9934eErik domain = ownerDomain; 23773714169d34a55fc01e14b30897a1ebbacf9934eErik } 23873714169d34a55fc01e14b30897a1ebbacf9934eErik } 23973714169d34a55fc01e14b30897a1ebbacf9934eErik } 24073714169d34a55fc01e14b30897a1ebbacf9934eErik mEmailValidator = new Rfc822Validator(domain); 24173714169d34a55fc01e14b30897a1ebbacf9934eErik } 24273714169d34a55fc01e14b30897a1ebbacf9934eErik 24373714169d34a55fc01e14b30897a1ebbacf9934eErik /** 244eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the event. Returns true if the event was successfully saved, false 245eca82e9468c002590045338f98588ad3ce1a0bb3Erik * otherwise. 24673714169d34a55fc01e14b30897a1ebbacf9934eErik * 24773714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The event model to save 24873714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalModel A model of the original event if it exists 24973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param modifyWhich For recurring events which type of series modification to use 25073714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if the event was successfully queued for saving 25173714169d34a55fc01e14b30897a1ebbacf9934eErik */ 25273714169d34a55fc01e14b30897a1ebbacf9934eErik public boolean saveEvent(CalendarEventModel model, CalendarEventModel originalModel, 25373714169d34a55fc01e14b30897a1ebbacf9934eErik int modifyWhich) { 25473714169d34a55fc01e14b30897a1ebbacf9934eErik boolean forceSaveReminders = false; 25573714169d34a55fc01e14b30897a1ebbacf9934eErik 25673714169d34a55fc01e14b30897a1ebbacf9934eErik if (DEBUG) { 25773714169d34a55fc01e14b30897a1ebbacf9934eErik Log.d(TAG, "Saving event model: " + model); 25873714169d34a55fc01e14b30897a1ebbacf9934eErik } 25973714169d34a55fc01e14b30897a1ebbacf9934eErik 26073714169d34a55fc01e14b30897a1ebbacf9934eErik if (!mEventOk) { 26173714169d34a55fc01e14b30897a1ebbacf9934eErik if (DEBUG) { 26273714169d34a55fc01e14b30897a1ebbacf9934eErik Log.w(TAG, "Event no longer exists. Event was not saved."); 26373714169d34a55fc01e14b30897a1ebbacf9934eErik } 26473714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 26573714169d34a55fc01e14b30897a1ebbacf9934eErik } 26673714169d34a55fc01e14b30897a1ebbacf9934eErik 26773714169d34a55fc01e14b30897a1ebbacf9934eErik // It's a problem if we try to save a non-existent or invalid model or if we're 26873714169d34a55fc01e14b30897a1ebbacf9934eErik // modifying an existing event and we have the wrong original model 26973714169d34a55fc01e14b30897a1ebbacf9934eErik if (model == null) { 27073714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Attempted to save null model."); 27173714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 27273714169d34a55fc01e14b30897a1ebbacf9934eErik } 27373714169d34a55fc01e14b30897a1ebbacf9934eErik if (!model.isValid()) { 27473714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Attempted to save invalid model."); 27573714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 27673714169d34a55fc01e14b30897a1ebbacf9934eErik } 27773714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null && !isSameEvent(model, originalModel)) { 278eca82e9468c002590045338f98588ad3ce1a0bb3Erik Log.e(TAG, "Attempted to update existing event but models didn't refer to the same " 279eca82e9468c002590045338f98588ad3ce1a0bb3Erik + "event."); 28073714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 28173714169d34a55fc01e14b30897a1ebbacf9934eErik } 28273714169d34a55fc01e14b30897a1ebbacf9934eErik 283eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO Check if anything has been changed and return false if it 284eca82e9468c002590045338f98588ad3ce1a0bb3Erik // hasn't. 28573714169d34a55fc01e14b30897a1ebbacf9934eErik 28673714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 28773714169d34a55fc01e14b30897a1ebbacf9934eErik int eventIdIndex = -1; 28873714169d34a55fc01e14b30897a1ebbacf9934eErik 28973714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = getContentValuesFromModel(model); 29073714169d34a55fc01e14b30897a1ebbacf9934eErik 2914ebfbd26b1bc2f351d7e1657ccef261b1504b88cErik if (model.mUri != null && originalModel == null) { 29273714169d34a55fc01e14b30897a1ebbacf9934eErik Log.e(TAG, "Existing event but no originalModel provided. Aborting save."); 29373714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 29473714169d34a55fc01e14b30897a1ebbacf9934eErik } 2954ebfbd26b1bc2f351d7e1657ccef261b1504b88cErik Uri uri = null; 2964ebfbd26b1bc2f351d7e1657ccef261b1504b88cErik if (model.mUri != null) { 2974ebfbd26b1bc2f351d7e1657ccef261b1504b88cErik uri = Uri.parse(model.mUri); 2984ebfbd26b1bc2f351d7e1657ccef261b1504b88cErik } 29973714169d34a55fc01e14b30897a1ebbacf9934eErik 30073714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the "hasAlarm" field for the event 30173714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> reminderMinutes = model.mReminderMinutes; 30273714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 30373714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0); 30473714169d34a55fc01e14b30897a1ebbacf9934eErik 30573714169d34a55fc01e14b30897a1ebbacf9934eErik if (uri == null) { 30673714169d34a55fc01e14b30897a1ebbacf9934eErik // Add hasAttendeeData for a new event 30773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.HAS_ATTENDEE_DATA, 1); 30873714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 309eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newInsert( 310eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.CONTENT_URI).withValues(values); 31173714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 31273714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 31373714169d34a55fc01e14b30897a1ebbacf9934eErik 31473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (model.mRrule == null && originalModel.mRrule == null) { 31573714169d34a55fc01e14b30897a1ebbacf9934eErik // Simple update to a non-recurring event 31673714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 31773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 31873714169d34a55fc01e14b30897a1ebbacf9934eErik 31973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (originalModel.mRrule == null) { 32073714169d34a55fc01e14b30897a1ebbacf9934eErik // This event was changed from a non-repeating event to a 32173714169d34a55fc01e14b30897a1ebbacf9934eErik // repeating event. 32273714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 32373714169d34a55fc01e14b30897a1ebbacf9934eErik 32473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_SELECTED) { 32573714169d34a55fc01e14b30897a1ebbacf9934eErik // Modify contents of the current instance of repeating event 32673714169d34a55fc01e14b30897a1ebbacf9934eErik // Create a recurrence exception 32773714169d34a55fc01e14b30897a1ebbacf9934eErik long begin = model.mOriginalStart; 32873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_EVENT, originalModel.mSyncId); 32973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_INSTANCE_TIME, begin); 33073714169d34a55fc01e14b30897a1ebbacf9934eErik boolean allDay = originalModel.mAllDay; 33173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0); 33273714169d34a55fc01e14b30897a1ebbacf9934eErik 33373714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 334eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newInsert( 335eca82e9468c002590045338f98588ad3ce1a0bb3Erik Events.CONTENT_URI).withValues(values); 33673714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 33773714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 33873714169d34a55fc01e14b30897a1ebbacf9934eErik 33973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_ALL_FOLLOWING) { 34073714169d34a55fc01e14b30897a1ebbacf9934eErik 34173714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mRrule == null) { 34273714169d34a55fc01e14b30897a1ebbacf9934eErik // We've changed a recurring event to a non-recurring event. 34373714169d34a55fc01e14b30897a1ebbacf9934eErik // If the event we are editing is the first in the series, 344eca82e9468c002590045338f98588ad3ce1a0bb3Erik // then delete the whole series. Otherwise, update the series 34573714169d34a55fc01e14b30897a1ebbacf9934eErik // to end at the new start time. 34673714169d34a55fc01e14b30897a1ebbacf9934eErik if (isFirstEventInSeries(model, originalModel)) { 34773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newDelete(uri).build()); 34873714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 34973714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the current repeating event to end at the new 35073714169d34a55fc01e14b30897a1ebbacf9934eErik // start time. 35173714169d34a55fc01e14b30897a1ebbacf9934eErik updatePastEvents(ops, originalModel, model.mOriginalStart); 35273714169d34a55fc01e14b30897a1ebbacf9934eErik } 35373714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 354eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values) 35573714169d34a55fc01e14b30897a1ebbacf9934eErik .build()); 35673714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 35773714169d34a55fc01e14b30897a1ebbacf9934eErik if (isFirstEventInSeries(model, originalModel)) { 35873714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 359eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate(uri) 36073714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 36173714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 36273714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 36373714169d34a55fc01e14b30897a1ebbacf9934eErik // Update the current repeating event to end at the new 36473714169d34a55fc01e14b30897a1ebbacf9934eErik // start time. 36573714169d34a55fc01e14b30897a1ebbacf9934eErik updatePastEvents(ops, originalModel, model.mOriginalStart); 36673714169d34a55fc01e14b30897a1ebbacf9934eErik 36773714169d34a55fc01e14b30897a1ebbacf9934eErik // Create a new event with the user-modified fields 36873714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 369eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues( 370eca82e9468c002590045338f98588ad3ce1a0bb3Erik values).build()); 37173714169d34a55fc01e14b30897a1ebbacf9934eErik } 37273714169d34a55fc01e14b30897a1ebbacf9934eErik } 37373714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 37473714169d34a55fc01e14b30897a1ebbacf9934eErik 37573714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (modifyWhich == MODIFY_ALL) { 37673714169d34a55fc01e14b30897a1ebbacf9934eErik 37773714169d34a55fc01e14b30897a1ebbacf9934eErik // Modify all instances of repeating event 37873714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mRrule == null) { 37973714169d34a55fc01e14b30897a1ebbacf9934eErik // We've changed a recurring event to a non-recurring event. 38073714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete the whole series and replace it with a new 38173714169d34a55fc01e14b30897a1ebbacf9934eErik // non-recurring event. 38273714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(ContentProviderOperation.newDelete(uri).build()); 38373714169d34a55fc01e14b30897a1ebbacf9934eErik 38473714169d34a55fc01e14b30897a1ebbacf9934eErik eventIdIndex = ops.size(); 385eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values) 38673714169d34a55fc01e14b30897a1ebbacf9934eErik .build()); 38773714169d34a55fc01e14b30897a1ebbacf9934eErik forceSaveReminders = true; 38873714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 38973714169d34a55fc01e14b30897a1ebbacf9934eErik checkTimeDependentFields(originalModel, model, values, modifyWhich); 390eca82e9468c002590045338f98588ad3ce1a0bb3Erik ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 39173714169d34a55fc01e14b30897a1ebbacf9934eErik } 39273714169d34a55fc01e14b30897a1ebbacf9934eErik } 39373714169d34a55fc01e14b30897a1ebbacf9934eErik 39473714169d34a55fc01e14b30897a1ebbacf9934eErik // New Event or New Exception to an existing event 39573714169d34a55fc01e14b30897a1ebbacf9934eErik boolean newEvent = (eventIdIndex != -1); 39673714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> originalMinutes; 39773714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null) { 39873714169d34a55fc01e14b30897a1ebbacf9934eErik originalMinutes = originalModel.mReminderMinutes; 39973714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 40073714169d34a55fc01e14b30897a1ebbacf9934eErik originalMinutes = new ArrayList<Integer>(); 40173714169d34a55fc01e14b30897a1ebbacf9934eErik } 40273714169d34a55fc01e14b30897a1ebbacf9934eErik 40373714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent) { 404eca82e9468c002590045338f98588ad3ce1a0bb3Erik saveRemindersWithBackRef(ops, eventIdIndex, reminderMinutes, originalMinutes, 405eca82e9468c002590045338f98588ad3ce1a0bb3Erik forceSaveReminders); 40673714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (uri != null) { 40773714169d34a55fc01e14b30897a1ebbacf9934eErik long eventId = ContentUris.parseId(uri); 408eca82e9468c002590045338f98588ad3ce1a0bb3Erik saveReminders(ops, eventId, reminderMinutes, originalMinutes, forceSaveReminders); 40973714169d34a55fc01e14b30897a1ebbacf9934eErik } 41073714169d34a55fc01e14b30897a1ebbacf9934eErik 41173714169d34a55fc01e14b30897a1ebbacf9934eErik ContentProviderOperation.Builder b; 41273714169d34a55fc01e14b30897a1ebbacf9934eErik boolean hasAttendeeData = model.mHasAttendeeData; 41373714169d34a55fc01e14b30897a1ebbacf9934eErik 41473714169d34a55fc01e14b30897a1ebbacf9934eErik // New event/instance - Set Organizer's response as yes 41573714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasAttendeeData && newEvent) { 41673714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 41773714169d34a55fc01e14b30897a1ebbacf9934eErik 41873714169d34a55fc01e14b30897a1ebbacf9934eErik String ownerEmail = model.mOwnerAccount; 41973714169d34a55fc01e14b30897a1ebbacf9934eErik if (ownerEmail != null) { 42073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.ATTENDEE_EMAIL, ownerEmail); 421eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER); 422eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 423e5149edad36737783356778819a9c66993a9cf28Michael Chan int initialStatus = Attendees.ATTENDEE_STATUS_ACCEPTED; 424e5149edad36737783356778819a9c66993a9cf28Michael Chan 425e5149edad36737783356778819a9c66993a9cf28Michael Chan // Don't accept for secondary calendars 426e5149edad36737783356778819a9c66993a9cf28Michael Chan if (ownerEmail.endsWith("calendar.google.com")) { 427e5149edad36737783356778819a9c66993a9cf28Michael Chan initialStatus = Attendees.ATTENDEE_STATUS_NONE; 428e5149edad36737783356778819a9c66993a9cf28Michael Chan } 429e5149edad36737783356778819a9c66993a9cf28Michael Chan values.put(Attendees.ATTENDEE_STATUS, initialStatus); 430eca82e9468c002590045338f98588ad3ce1a0bb3Erik 431eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values); 432d845fbe558d5229102b58cce70a1b29fe6cb6967Erik b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex); 43373714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 43473714169d34a55fc01e14b30897a1ebbacf9934eErik } 435d845fbe558d5229102b58cce70a1b29fe6cb6967Erik } else if (hasAttendeeData && 436d845fbe558d5229102b58cce70a1b29fe6cb6967Erik model.mSelfAttendeeStatus != originalModel.mSelfAttendeeStatus && 437d845fbe558d5229102b58cce70a1b29fe6cb6967Erik model.mOwnerAttendeeId != -1) { 438d845fbe558d5229102b58cce70a1b29fe6cb6967Erik if (DEBUG) { 439d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Log.d(TAG, "Setting attendee status to " + model.mSelfAttendeeStatus); 440d845fbe558d5229102b58cce70a1b29fe6cb6967Erik } 441d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Uri attUri = ContentUris.withAppendedId(Attendees.CONTENT_URI, model.mOwnerAttendeeId); 442d845fbe558d5229102b58cce70a1b29fe6cb6967Erik 443d845fbe558d5229102b58cce70a1b29fe6cb6967Erik values.clear(); 444d845fbe558d5229102b58cce70a1b29fe6cb6967Erik values.put(Attendees.ATTENDEE_STATUS, model.mSelfAttendeeStatus); 445d845fbe558d5229102b58cce70a1b29fe6cb6967Erik values.put(Attendees.EVENT_ID, model.mId); 446d845fbe558d5229102b58cce70a1b29fe6cb6967Erik b = ContentProviderOperation.newUpdate(attUri).withValues(values); 447d845fbe558d5229102b58cce70a1b29fe6cb6967Erik ops.add(b.build()); 44873714169d34a55fc01e14b30897a1ebbacf9934eErik } 44973714169d34a55fc01e14b30897a1ebbacf9934eErik 450eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO: is this the right test? this currently checks if this is 451eca82e9468c002590045338f98588ad3ce1a0bb3Erik // a new event or an existing event. or is this a paranoia check? 45273714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasAttendeeData && (newEvent || uri != null)) { 453d845fbe558d5229102b58cce70a1b29fe6cb6967Erik String attendees = model.getAttendeesString(); 45473714169d34a55fc01e14b30897a1ebbacf9934eErik String originalAttendeesString; 45573714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel != null) { 456d845fbe558d5229102b58cce70a1b29fe6cb6967Erik originalAttendeesString = originalModel.getAttendeesString(); 45773714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 45873714169d34a55fc01e14b30897a1ebbacf9934eErik originalAttendeesString = ""; 45973714169d34a55fc01e14b30897a1ebbacf9934eErik } 460eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Hit the content provider only if this is a new event or the user 461eca82e9468c002590045338f98588ad3ce1a0bb3Erik // has changed it 46273714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent || !TextUtils.equals(originalAttendeesString, attendees)) { 46373714169d34a55fc01e14b30897a1ebbacf9934eErik // figure out which attendees need to be added and which ones 464eca82e9468c002590045338f98588ad3ce1a0bb3Erik // need to be deleted. use a linked hash set, so we maintain 46573714169d34a55fc01e14b30897a1ebbacf9934eErik // order (but also remove duplicates). 466eca82e9468c002590045338f98588ad3ce1a0bb3Erik setDomainFromModel(model); 467d845fbe558d5229102b58cce70a1b29fe6cb6967Erik HashMap<String, Attendee> newAttendees = model.mAttendeesList; 468d845fbe558d5229102b58cce70a1b29fe6cb6967Erik LinkedList<String> removedAttendees = new LinkedList<String>(); 46973714169d34a55fc01e14b30897a1ebbacf9934eErik 47073714169d34a55fc01e14b30897a1ebbacf9934eErik // the eventId is only used if eventIdIndex is -1. 47173714169d34a55fc01e14b30897a1ebbacf9934eErik // TODO: clean up this code. 47273714169d34a55fc01e14b30897a1ebbacf9934eErik long eventId = uri != null ? ContentUris.parseId(uri) : -1; 47373714169d34a55fc01e14b30897a1ebbacf9934eErik 47473714169d34a55fc01e14b30897a1ebbacf9934eErik // only compute deltas if this is an existing event. 47573714169d34a55fc01e14b30897a1ebbacf9934eErik // new events (being inserted into the Events table) won't 47673714169d34a55fc01e14b30897a1ebbacf9934eErik // have any existing attendees. 47773714169d34a55fc01e14b30897a1ebbacf9934eErik if (!newEvent) { 478d845fbe558d5229102b58cce70a1b29fe6cb6967Erik removedAttendees.clear(); 479d845fbe558d5229102b58cce70a1b29fe6cb6967Erik HashMap<String, Attendee> originalAttendees = originalModel.mAttendeesList; 480d845fbe558d5229102b58cce70a1b29fe6cb6967Erik for (String originalEmail : originalAttendees.keySet()) { 481d845fbe558d5229102b58cce70a1b29fe6cb6967Erik if (newAttendees.containsKey(originalEmail)) { 482eca82e9468c002590045338f98588ad3ce1a0bb3Erik // existing attendee. remove from new attendees set. 483d845fbe558d5229102b58cce70a1b29fe6cb6967Erik newAttendees.remove(originalEmail); 48473714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 485eca82e9468c002590045338f98588ad3ce1a0bb3Erik // no longer in attendees. mark as removed. 486d845fbe558d5229102b58cce70a1b29fe6cb6967Erik removedAttendees.add(originalEmail); 48773714169d34a55fc01e14b30897a1ebbacf9934eErik } 48873714169d34a55fc01e14b30897a1ebbacf9934eErik } 48973714169d34a55fc01e14b30897a1ebbacf9934eErik 49073714169d34a55fc01e14b30897a1ebbacf9934eErik // delete removed attendees if necessary 49173714169d34a55fc01e14b30897a1ebbacf9934eErik if (removedAttendees.size() > 0) { 49273714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newDelete(Attendees.CONTENT_URI); 49373714169d34a55fc01e14b30897a1ebbacf9934eErik 49473714169d34a55fc01e14b30897a1ebbacf9934eErik String[] args = new String[removedAttendees.size() + 1]; 49573714169d34a55fc01e14b30897a1ebbacf9934eErik args[0] = Long.toString(eventId); 49673714169d34a55fc01e14b30897a1ebbacf9934eErik int i = 1; 49773714169d34a55fc01e14b30897a1ebbacf9934eErik StringBuilder deleteWhere = new StringBuilder(ATTENDEES_DELETE_PREFIX); 498d845fbe558d5229102b58cce70a1b29fe6cb6967Erik for (String removedAttendee : removedAttendees) { 49973714169d34a55fc01e14b30897a1ebbacf9934eErik if (i > 1) { 50073714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append(","); 50173714169d34a55fc01e14b30897a1ebbacf9934eErik } 50273714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append("?"); 503d845fbe558d5229102b58cce70a1b29fe6cb6967Erik args[i++] = removedAttendee; 50473714169d34a55fc01e14b30897a1ebbacf9934eErik } 50573714169d34a55fc01e14b30897a1ebbacf9934eErik deleteWhere.append(")"); 50673714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(deleteWhere.toString(), args); 50773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 50873714169d34a55fc01e14b30897a1ebbacf9934eErik } 50973714169d34a55fc01e14b30897a1ebbacf9934eErik } 51073714169d34a55fc01e14b30897a1ebbacf9934eErik 51173714169d34a55fc01e14b30897a1ebbacf9934eErik if (newAttendees.size() > 0) { 51273714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new attendees 513d845fbe558d5229102b58cce70a1b29fe6cb6967Erik for (Attendee attendee : newAttendees.values()) { 51473714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 515d845fbe558d5229102b58cce70a1b29fe6cb6967Erik values.put(Attendees.ATTENDEE_NAME, attendee.mName); 516d845fbe558d5229102b58cce70a1b29fe6cb6967Erik values.put(Attendees.ATTENDEE_EMAIL, attendee.mEmail); 51773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.ATTENDEE_RELATIONSHIP, 51873714169d34a55fc01e14b30897a1ebbacf9934eErik Attendees.RELATIONSHIP_ATTENDEE); 519eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 520eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); 52173714169d34a55fc01e14b30897a1ebbacf9934eErik 52273714169d34a55fc01e14b30897a1ebbacf9934eErik if (newEvent) { 523eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI) 52473714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 52573714169d34a55fc01e14b30897a1ebbacf9934eErik b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex); 52673714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 52773714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Attendees.EVENT_ID, eventId); 528eca82e9468c002590045338f98588ad3ce1a0bb3Erik b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI) 52973714169d34a55fc01e14b30897a1ebbacf9934eErik .withValues(values); 53073714169d34a55fc01e14b30897a1ebbacf9934eErik } 53173714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 53273714169d34a55fc01e14b30897a1ebbacf9934eErik } 53373714169d34a55fc01e14b30897a1ebbacf9934eErik } 53473714169d34a55fc01e14b30897a1ebbacf9934eErik } 53573714169d34a55fc01e14b30897a1ebbacf9934eErik } 53673714169d34a55fc01e14b30897a1ebbacf9934eErik 53737960c091b5912dd45beb561a85a9064ea42e616Erik 53837960c091b5912dd45beb561a85a9064ea42e616Erik mService.startBatch(mService.getNextToken(), null, android.provider.Calendar.AUTHORITY, ops, 53937960c091b5912dd45beb561a85a9064ea42e616Erik Utils.UNDO_DELAY); 54073714169d34a55fc01e14b30897a1ebbacf9934eErik 54173714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 54273714169d34a55fc01e14b30897a1ebbacf9934eErik } 54373714169d34a55fc01e14b30897a1ebbacf9934eErik 544d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public static LinkedHashSet<Rfc822Token> getAddressesFromList(String list, 545d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Rfc822Validator validator) { 54673714169d34a55fc01e14b30897a1ebbacf9934eErik LinkedHashSet<Rfc822Token> addresses = new LinkedHashSet<Rfc822Token>(); 54773714169d34a55fc01e14b30897a1ebbacf9934eErik Rfc822Tokenizer.tokenize(list, addresses); 548d845fbe558d5229102b58cce70a1b29fe6cb6967Erik if (validator == null) { 549d845fbe558d5229102b58cce70a1b29fe6cb6967Erik return addresses; 550d845fbe558d5229102b58cce70a1b29fe6cb6967Erik } 55173714169d34a55fc01e14b30897a1ebbacf9934eErik 552eca82e9468c002590045338f98588ad3ce1a0bb3Erik // validate the emails, out of paranoia. they should already be 55373714169d34a55fc01e14b30897a1ebbacf9934eErik // validated on input, but drop any invalid emails just to be safe. 55473714169d34a55fc01e14b30897a1ebbacf9934eErik Iterator<Rfc822Token> addressIterator = addresses.iterator(); 55573714169d34a55fc01e14b30897a1ebbacf9934eErik while (addressIterator.hasNext()) { 55673714169d34a55fc01e14b30897a1ebbacf9934eErik Rfc822Token address = addressIterator.next(); 557d845fbe558d5229102b58cce70a1b29fe6cb6967Erik if (!validator.isValid(address.getAddress())) { 558d845fbe558d5229102b58cce70a1b29fe6cb6967Erik Log.v(TAG, "Dropping invalid attendee email address: " + address.getAddress()); 55973714169d34a55fc01e14b30897a1ebbacf9934eErik addressIterator.remove(); 56073714169d34a55fc01e14b30897a1ebbacf9934eErik } 56173714169d34a55fc01e14b30897a1ebbacf9934eErik } 56273714169d34a55fc01e14b30897a1ebbacf9934eErik return addresses; 56373714169d34a55fc01e14b30897a1ebbacf9934eErik } 56473714169d34a55fc01e14b30897a1ebbacf9934eErik 565e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang /** 566e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * When we aren't given an explicit start time, we default to the next 567e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * upcoming half hour. So, for example, 5:01 -> 5:30, 5:30 -> 6:00, etc. 568e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * 569e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * @return a UTC time in milliseconds representing the next upcoming half 570e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * hour 571e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang */ 572e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang protected long constructDefaultStartTime(long now) { 573e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang Time defaultStart = new Time(); 574e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang defaultStart.set(now); 575e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang defaultStart.second = 0; 576e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang defaultStart.minute = 30; 577e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang long defaultStartMillis = defaultStart.toMillis(false); 578e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang if (now < defaultStartMillis) { 579e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang return defaultStartMillis; 580e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang } else { 581e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang return defaultStartMillis + 30 * DateUtils.MINUTE_IN_MILLIS; 582e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang } 583e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang } 584e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang 585e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang /** 586e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * When we aren't given an explicit end time, we default to an hour after 587e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * the start time. 588e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * @param startTime the start time 589e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang * @return a default end time 590e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang */ 591e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang protected long constructDefaultEndTime(long startTime) { 592e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang return startTime + DateUtils.HOUR_IN_MILLIS; 593e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang } 594e700c16ec2464cbba86f91f8f757ae59cbed34b0Mason Tang 595eca82e9468c002590045338f98588ad3ce1a0bb3Erik // TODO think about how useful this is. Probably check if our event has 596eca82e9468c002590045338f98588ad3ce1a0bb3Erik // changed early on and either update all or nothing. Should still do the if 597eca82e9468c002590045338f98588ad3ce1a0bb3Erik // MODIFY_ALL bit. 598eca82e9468c002590045338f98588ad3ce1a0bb3Erik void checkTimeDependentFields(CalendarEventModel originalModel, CalendarEventModel model, 599eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentValues values, int modifyWhich) { 60073714169d34a55fc01e14b30897a1ebbacf9934eErik long oldBegin = model.mOriginalStart; 60173714169d34a55fc01e14b30897a1ebbacf9934eErik long oldEnd = model.mOriginalEnd; 60273714169d34a55fc01e14b30897a1ebbacf9934eErik boolean oldAllDay = originalModel.mAllDay; 60373714169d34a55fc01e14b30897a1ebbacf9934eErik String oldRrule = originalModel.mRrule; 60473714169d34a55fc01e14b30897a1ebbacf9934eErik String oldTimezone = originalModel.mTimezone; 60573714169d34a55fc01e14b30897a1ebbacf9934eErik 60673714169d34a55fc01e14b30897a1ebbacf9934eErik long newBegin = model.mStart; 60773714169d34a55fc01e14b30897a1ebbacf9934eErik long newEnd = model.mEnd; 60873714169d34a55fc01e14b30897a1ebbacf9934eErik boolean newAllDay = model.mAllDay; 60973714169d34a55fc01e14b30897a1ebbacf9934eErik String newRrule = model.mRrule; 61073714169d34a55fc01e14b30897a1ebbacf9934eErik String newTimezone = model.mTimezone; 61173714169d34a55fc01e14b30897a1ebbacf9934eErik 61273714169d34a55fc01e14b30897a1ebbacf9934eErik // If none of the time-dependent fields changed, then remove them. 61373714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldBegin == newBegin && oldEnd == newEnd && oldAllDay == newAllDay 61473714169d34a55fc01e14b30897a1ebbacf9934eErik && TextUtils.equals(oldRrule, newRrule) 615eca82e9468c002590045338f98588ad3ce1a0bb3Erik && TextUtils.equals(oldTimezone, newTimezone)) { 61673714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DTSTART); 61773714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DTEND); 61873714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.DURATION); 61973714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.ALL_DAY); 62073714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.RRULE); 62173714169d34a55fc01e14b30897a1ebbacf9934eErik values.remove(Events.EVENT_TIMEZONE); 62273714169d34a55fc01e14b30897a1ebbacf9934eErik return; 62373714169d34a55fc01e14b30897a1ebbacf9934eErik } 62473714169d34a55fc01e14b30897a1ebbacf9934eErik 62573714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldRrule == null || newRrule == null) { 62673714169d34a55fc01e14b30897a1ebbacf9934eErik return; 62773714169d34a55fc01e14b30897a1ebbacf9934eErik } 62873714169d34a55fc01e14b30897a1ebbacf9934eErik 62973714169d34a55fc01e14b30897a1ebbacf9934eErik // If we are modifying all events then we need to set DTSTART to the 63073714169d34a55fc01e14b30897a1ebbacf9934eErik // start time of the first event in the series, not the current 631eca82e9468c002590045338f98588ad3ce1a0bb3Erik // date and time. If the start time of the event was changed 63273714169d34a55fc01e14b30897a1ebbacf9934eErik // (from, say, 3pm to 4pm), then we want to add the time difference 63373714169d34a55fc01e14b30897a1ebbacf9934eErik // to the start time of the first event in the series (the DTSTART 634eca82e9468c002590045338f98588ad3ce1a0bb3Erik // value). If we are modifying one instance or all following instances, 63573714169d34a55fc01e14b30897a1ebbacf9934eErik // then we leave the DTSTART field alone. 63673714169d34a55fc01e14b30897a1ebbacf9934eErik if (modifyWhich == MODIFY_ALL) { 63773714169d34a55fc01e14b30897a1ebbacf9934eErik long oldStartMillis = originalModel.mStart; 63873714169d34a55fc01e14b30897a1ebbacf9934eErik long oldStartMillis2 = oldStartMillis; 63973714169d34a55fc01e14b30897a1ebbacf9934eErik if (oldBegin != newBegin) { 64073714169d34a55fc01e14b30897a1ebbacf9934eErik // The user changed the start time of this event 64173714169d34a55fc01e14b30897a1ebbacf9934eErik long offset = newBegin - oldBegin; 64273714169d34a55fc01e14b30897a1ebbacf9934eErik oldStartMillis += offset; 64373714169d34a55fc01e14b30897a1ebbacf9934eErik } 64473714169d34a55fc01e14b30897a1ebbacf9934eErik if (newAllDay) { 64573714169d34a55fc01e14b30897a1ebbacf9934eErik Time time = new Time(Time.TIMEZONE_UTC); 64673714169d34a55fc01e14b30897a1ebbacf9934eErik time.set(oldStartMillis); 64773714169d34a55fc01e14b30897a1ebbacf9934eErik time.hour = 0; 64873714169d34a55fc01e14b30897a1ebbacf9934eErik time.minute = 0; 64973714169d34a55fc01e14b30897a1ebbacf9934eErik time.second = 0; 65073714169d34a55fc01e14b30897a1ebbacf9934eErik oldStartMillis = time.toMillis(false); 65173714169d34a55fc01e14b30897a1ebbacf9934eErik } 65273714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTSTART, oldStartMillis); 65373714169d34a55fc01e14b30897a1ebbacf9934eErik } 65473714169d34a55fc01e14b30897a1ebbacf9934eErik } 65573714169d34a55fc01e14b30897a1ebbacf9934eErik 65673714169d34a55fc01e14b30897a1ebbacf9934eErik /** 657eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Prepares an update to the original event so it stops where the new series 658eca82e9468c002590045338f98588ad3ce1a0bb3Erik * begins When we update 'this and all following' events we need to change 659eca82e9468c002590045338f98588ad3ce1a0bb3Erik * the original event to end before a new series starts. This creates an 660eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update to the old event's rrule to do that. 66173714169d34a55fc01e14b30897a1ebbacf9934eErik * 66273714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops The list of operations to add the update to 66373714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalModel The original event that we're updating 66473714169d34a55fc01e14b30897a1ebbacf9934eErik * @param initialBeginTime The original start time for the exception 66573714169d34a55fc01e14b30897a1ebbacf9934eErik */ 666fbce65e53c7a111955f638db5bf8bee35381e5b7Erik public void updatePastEvents(ArrayList<ContentProviderOperation> ops, 66773714169d34a55fc01e14b30897a1ebbacf9934eErik CalendarEventModel originalModel, long initialBeginTime) { 66873714169d34a55fc01e14b30897a1ebbacf9934eErik boolean allDay = originalModel.mAllDay; 66973714169d34a55fc01e14b30897a1ebbacf9934eErik String oldRrule = originalModel.mRrule; 670eca82e9468c002590045338f98588ad3ce1a0bb3Erik 67173714169d34a55fc01e14b30897a1ebbacf9934eErik EventRecurrence eventRecurrence = new EventRecurrence(); 67273714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.parse(oldRrule); 67373714169d34a55fc01e14b30897a1ebbacf9934eErik 67473714169d34a55fc01e14b30897a1ebbacf9934eErik Time untilTime = new Time(); 675eca82e9468c002590045338f98588ad3ce1a0bb3Erik Time dtstart = new Time(); 67673714169d34a55fc01e14b30897a1ebbacf9934eErik long begin = initialBeginTime; 67773714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues oldValues = new ContentValues(); 67873714169d34a55fc01e14b30897a1ebbacf9934eErik 67973714169d34a55fc01e14b30897a1ebbacf9934eErik // The "until" time must be in UTC time in order for Google calendar 680eca82e9468c002590045338f98588ad3ce1a0bb3Erik // to display it properly. For all-day events, the "until" time string 681eca82e9468c002590045338f98588ad3ce1a0bb3Erik // must include just the date field, and not the time field. The 68273714169d34a55fc01e14b30897a1ebbacf9934eErik // repeating events repeat up to and including the "until" time. 68373714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.timezone = Time.TIMEZONE_UTC; 684eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.timezone = originalModel.mTimezone; 685eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.set(originalModel.mStart); 68673714169d34a55fc01e14b30897a1ebbacf9934eErik 68773714169d34a55fc01e14b30897a1ebbacf9934eErik // Subtract one second from the old begin time to get the new 68873714169d34a55fc01e14b30897a1ebbacf9934eErik // "until" time. 689eca82e9468c002590045338f98588ad3ce1a0bb3Erik untilTime.set(begin - 1000); // subtract one second (1000 millis) 69073714169d34a55fc01e14b30897a1ebbacf9934eErik if (allDay) { 69173714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.hour = 0; 69273714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.minute = 0; 69373714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.second = 0; 69473714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.allDay = true; 69573714169d34a55fc01e14b30897a1ebbacf9934eErik untilTime.normalize(false); 696eca82e9468c002590045338f98588ad3ce1a0bb3Erik 697eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.hour = 0; 698eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.minute = 0; 699eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.second = 0; 700eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.allDay = true; 701eca82e9468c002590045338f98588ad3ce1a0bb3Erik dtstart.timezone = Time.TIMEZONE_UTC; 70273714169d34a55fc01e14b30897a1ebbacf9934eErik } 70373714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.until = untilTime.format2445(); 70473714169d34a55fc01e14b30897a1ebbacf9934eErik 70573714169d34a55fc01e14b30897a1ebbacf9934eErik oldValues.put(Events.RRULE, eventRecurrence.toString()); 706eca82e9468c002590045338f98588ad3ce1a0bb3Erik oldValues.put(Events.DTSTART, dtstart.normalize(true)); 70759ead671b50514b49749fcd6a1d4186a270239d2Erik ContentProviderOperation.Builder b = 70859ead671b50514b49749fcd6a1d4186a270239d2Erik ContentProviderOperation.newUpdate(Uri.parse(originalModel.mUri)) 709eca82e9468c002590045338f98588ad3ce1a0bb3Erik .withValues(oldValues); 71073714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 71173714169d34a55fc01e14b30897a1ebbacf9934eErik } 71273714169d34a55fc01e14b30897a1ebbacf9934eErik 713eca82e9468c002590045338f98588ad3ce1a0bb3Erik // Constructs a label given an arbitrary number of minutes. For example, 71473714169d34a55fc01e14b30897a1ebbacf9934eErik // if the given minutes is 63, then this returns the string "63 minutes". 71573714169d34a55fc01e14b30897a1ebbacf9934eErik // As another example, if the given minutes is 120, then this returns 71673714169d34a55fc01e14b30897a1ebbacf9934eErik // "2 hours". 717fbce65e53c7a111955f638db5bf8bee35381e5b7Erik public String constructReminderLabel(int minutes, boolean abbrev) { 718dd95df57c8c5a58a85c4c0effad5652dec14f621Erik Resources resources = mContext.getResources(); 71973714169d34a55fc01e14b30897a1ebbacf9934eErik int value, resId; 72073714169d34a55fc01e14b30897a1ebbacf9934eErik 72173714169d34a55fc01e14b30897a1ebbacf9934eErik if (minutes % 60 != 0) { 72273714169d34a55fc01e14b30897a1ebbacf9934eErik value = minutes; 72373714169d34a55fc01e14b30897a1ebbacf9934eErik if (abbrev) { 72473714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nmins; 72573714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 72673714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nminutes; 72773714169d34a55fc01e14b30897a1ebbacf9934eErik } 72873714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (minutes % (24 * 60) != 0) { 72973714169d34a55fc01e14b30897a1ebbacf9934eErik value = minutes / 60; 73073714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Nhours; 73173714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 732eca82e9468c002590045338f98588ad3ce1a0bb3Erik value = minutes / (24 * 60); 73373714169d34a55fc01e14b30897a1ebbacf9934eErik resId = R.plurals.Ndays; 73473714169d34a55fc01e14b30897a1ebbacf9934eErik } 73573714169d34a55fc01e14b30897a1ebbacf9934eErik 73673714169d34a55fc01e14b30897a1ebbacf9934eErik String format = resources.getQuantityString(resId, value); 73773714169d34a55fc01e14b30897a1ebbacf9934eErik return String.format(format, value); 73873714169d34a55fc01e14b30897a1ebbacf9934eErik } 73973714169d34a55fc01e14b30897a1ebbacf9934eErik 74073714169d34a55fc01e14b30897a1ebbacf9934eErik /** 741eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Compares two models to ensure that they refer to the same event. This is 742eca82e9468c002590045338f98588ad3ce1a0bb3Erik * a safety check to make sure an updated event model refers to the same 743eca82e9468c002590045338f98588ad3ce1a0bb3Erik * event as the original model. If the original model is null then this is a 744eca82e9468c002590045338f98588ad3ce1a0bb3Erik * new event or we're forcing an overwrite so we return true in that case. 745eca82e9468c002590045338f98588ad3ce1a0bb3Erik * The important identifiers are the Calendar Id and the Event Id. 74673714169d34a55fc01e14b30897a1ebbacf9934eErik * 74773714169d34a55fc01e14b30897a1ebbacf9934eErik * @return 74873714169d34a55fc01e14b30897a1ebbacf9934eErik */ 749fbce65e53c7a111955f638db5bf8bee35381e5b7Erik public static boolean isSameEvent(CalendarEventModel model, CalendarEventModel originalModel) { 75073714169d34a55fc01e14b30897a1ebbacf9934eErik if (originalModel == null) { 75173714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 75273714169d34a55fc01e14b30897a1ebbacf9934eErik } 75373714169d34a55fc01e14b30897a1ebbacf9934eErik 75473714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mCalendarId != originalModel.mCalendarId) { 75573714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 75673714169d34a55fc01e14b30897a1ebbacf9934eErik } 75773714169d34a55fc01e14b30897a1ebbacf9934eErik if (model.mId != originalModel.mId) { 75873714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 75973714169d34a55fc01e14b30897a1ebbacf9934eErik } 76073714169d34a55fc01e14b30897a1ebbacf9934eErik 76173714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 76273714169d34a55fc01e14b30897a1ebbacf9934eErik } 76373714169d34a55fc01e14b30897a1ebbacf9934eErik 764eca82e9468c002590045338f98588ad3ce1a0bb3Erik /** 765eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the reminders, if they changed. Returns true if operations to 766eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update the database were added. 76773714169d34a55fc01e14b30897a1ebbacf9934eErik * 76873714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops the array of ContentProviderOperations 76973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param eventId the id of the event whose reminders are being updated 77073714169d34a55fc01e14b30897a1ebbacf9934eErik * @param reminderMinutes the array of reminders set by the user 77173714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalMinutes the original array of reminders 772eca82e9468c002590045338f98588ad3ce1a0bb3Erik * @param forceSave if true, then save the reminders even if they didn't change 77373714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if operations to update the database were added 77473714169d34a55fc01e14b30897a1ebbacf9934eErik */ 775fbce65e53c7a111955f638db5bf8bee35381e5b7Erik public static boolean saveReminders(ArrayList<ContentProviderOperation> ops, long eventId, 77673714169d34a55fc01e14b30897a1ebbacf9934eErik ArrayList<Integer> reminderMinutes, ArrayList<Integer> originalMinutes, 77773714169d34a55fc01e14b30897a1ebbacf9934eErik boolean forceSave) { 77873714169d34a55fc01e14b30897a1ebbacf9934eErik // If the reminders have not changed, then don't update the database 77973714169d34a55fc01e14b30897a1ebbacf9934eErik if (reminderMinutes.equals(originalMinutes) && !forceSave) { 78073714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 78173714169d34a55fc01e14b30897a1ebbacf9934eErik } 78273714169d34a55fc01e14b30897a1ebbacf9934eErik 78373714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete all the existing reminders for this event 78473714169d34a55fc01e14b30897a1ebbacf9934eErik String where = Reminders.EVENT_ID + "=?"; 78573714169d34a55fc01e14b30897a1ebbacf9934eErik String[] args = new String[] {Long.toString(eventId)}; 786eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation 787eca82e9468c002590045338f98588ad3ce1a0bb3Erik .newDelete(Reminders.CONTENT_URI); 78873714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(where, args); 78973714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 79073714169d34a55fc01e14b30897a1ebbacf9934eErik 79173714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 79273714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 79373714169d34a55fc01e14b30897a1ebbacf9934eErik 79473714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new reminders, if any 79573714169d34a55fc01e14b30897a1ebbacf9934eErik for (int i = 0; i < len; i++) { 79673714169d34a55fc01e14b30897a1ebbacf9934eErik int minutes = reminderMinutes.get(i); 79773714169d34a55fc01e14b30897a1ebbacf9934eErik 79873714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 79973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.MINUTES, minutes); 80073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.METHOD, Reminders.METHOD_ALERT); 80173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.EVENT_ID, eventId); 80273714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values); 80373714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 80473714169d34a55fc01e14b30897a1ebbacf9934eErik } 80573714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 80673714169d34a55fc01e14b30897a1ebbacf9934eErik } 80773714169d34a55fc01e14b30897a1ebbacf9934eErik 80873714169d34a55fc01e14b30897a1ebbacf9934eErik /** 809eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Saves the reminders, if they changed. Returns true if operations to 810eca82e9468c002590045338f98588ad3ce1a0bb3Erik * update the database were added. Uses a reference id since an id isn't 811eca82e9468c002590045338f98588ad3ce1a0bb3Erik * created until the row is added. 81273714169d34a55fc01e14b30897a1ebbacf9934eErik * 81373714169d34a55fc01e14b30897a1ebbacf9934eErik * @param ops the array of ContentProviderOperations 81473714169d34a55fc01e14b30897a1ebbacf9934eErik * @param eventId the id of the event whose reminders are being updated 81573714169d34a55fc01e14b30897a1ebbacf9934eErik * @param reminderMinutes the array of reminders set by the user 81673714169d34a55fc01e14b30897a1ebbacf9934eErik * @param originalMinutes the original array of reminders 817eca82e9468c002590045338f98588ad3ce1a0bb3Erik * @param forceSave if true, then save the reminders even if they didn't change 81873714169d34a55fc01e14b30897a1ebbacf9934eErik * @return true if operations to update the database were added 81973714169d34a55fc01e14b30897a1ebbacf9934eErik */ 820d845fbe558d5229102b58cce70a1b29fe6cb6967Erik public boolean saveRemindersWithBackRef(ArrayList<ContentProviderOperation> ops, 821d845fbe558d5229102b58cce70a1b29fe6cb6967Erik int eventIdIndex, ArrayList<Integer> reminderMinutes, 822d845fbe558d5229102b58cce70a1b29fe6cb6967Erik ArrayList<Integer> originalMinutes, boolean forceSave) { 82373714169d34a55fc01e14b30897a1ebbacf9934eErik // If the reminders have not changed, then don't update the database 82473714169d34a55fc01e14b30897a1ebbacf9934eErik if (reminderMinutes.equals(originalMinutes) && !forceSave) { 82573714169d34a55fc01e14b30897a1ebbacf9934eErik return false; 82673714169d34a55fc01e14b30897a1ebbacf9934eErik } 82773714169d34a55fc01e14b30897a1ebbacf9934eErik 82873714169d34a55fc01e14b30897a1ebbacf9934eErik // Delete all the existing reminders for this event 829eca82e9468c002590045338f98588ad3ce1a0bb3Erik ContentProviderOperation.Builder b = ContentProviderOperation 830eca82e9468c002590045338f98588ad3ce1a0bb3Erik .newDelete(Reminders.CONTENT_URI); 83173714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelection(Reminders.EVENT_ID + "=?", new String[1]); 83273714169d34a55fc01e14b30897a1ebbacf9934eErik b.withSelectionBackReference(0, eventIdIndex); 83373714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 83473714169d34a55fc01e14b30897a1ebbacf9934eErik 83573714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 83673714169d34a55fc01e14b30897a1ebbacf9934eErik int len = reminderMinutes.size(); 83773714169d34a55fc01e14b30897a1ebbacf9934eErik 83873714169d34a55fc01e14b30897a1ebbacf9934eErik // Insert the new reminders, if any 83973714169d34a55fc01e14b30897a1ebbacf9934eErik for (int i = 0; i < len; i++) { 84073714169d34a55fc01e14b30897a1ebbacf9934eErik int minutes = reminderMinutes.get(i); 84173714169d34a55fc01e14b30897a1ebbacf9934eErik 84273714169d34a55fc01e14b30897a1ebbacf9934eErik values.clear(); 84373714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.MINUTES, minutes); 84473714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Reminders.METHOD, Reminders.METHOD_ALERT); 84573714169d34a55fc01e14b30897a1ebbacf9934eErik b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values); 84673714169d34a55fc01e14b30897a1ebbacf9934eErik b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex); 84773714169d34a55fc01e14b30897a1ebbacf9934eErik ops.add(b.build()); 84873714169d34a55fc01e14b30897a1ebbacf9934eErik } 84973714169d34a55fc01e14b30897a1ebbacf9934eErik return true; 85073714169d34a55fc01e14b30897a1ebbacf9934eErik } 85173714169d34a55fc01e14b30897a1ebbacf9934eErik 852eca82e9468c002590045338f98588ad3ce1a0bb3Erik // It's the first event in the series if the start time before being 853eca82e9468c002590045338f98588ad3ce1a0bb3Erik // modified is the same as the original event's start time 85473714169d34a55fc01e14b30897a1ebbacf9934eErik static boolean isFirstEventInSeries(CalendarEventModel model, 85573714169d34a55fc01e14b30897a1ebbacf9934eErik CalendarEventModel originalModel) { 85673714169d34a55fc01e14b30897a1ebbacf9934eErik return model.mOriginalStart == originalModel.mStart; 85773714169d34a55fc01e14b30897a1ebbacf9934eErik } 85873714169d34a55fc01e14b30897a1ebbacf9934eErik 85973714169d34a55fc01e14b30897a1ebbacf9934eErik // Adds an rRule and duration to a set of content values 86073714169d34a55fc01e14b30897a1ebbacf9934eErik void addRecurrenceRule(ContentValues values, CalendarEventModel model) { 86173714169d34a55fc01e14b30897a1ebbacf9934eErik String rrule = model.mRrule; 86273714169d34a55fc01e14b30897a1ebbacf9934eErik 86373714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.RRULE, rrule); 86473714169d34a55fc01e14b30897a1ebbacf9934eErik long end = model.mEnd; 86573714169d34a55fc01e14b30897a1ebbacf9934eErik long start = model.mStart; 86673714169d34a55fc01e14b30897a1ebbacf9934eErik String duration = model.mDuration; 86773714169d34a55fc01e14b30897a1ebbacf9934eErik 86837960c091b5912dd45beb561a85a9064ea42e616Erik boolean isAllDay = model.mAllDay; 86937960c091b5912dd45beb561a85a9064ea42e616Erik if (end > start) { 87037960c091b5912dd45beb561a85a9064ea42e616Erik if (isAllDay) { 87137960c091b5912dd45beb561a85a9064ea42e616Erik // if it's all day compute the duration in days 87237960c091b5912dd45beb561a85a9064ea42e616Erik long days = (end - start + DateUtils.DAY_IN_MILLIS - 1) 87337960c091b5912dd45beb561a85a9064ea42e616Erik / DateUtils.DAY_IN_MILLIS; 87437960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P" + days + "D"; 87573714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 87637960c091b5912dd45beb561a85a9064ea42e616Erik // otherwise compute the duration in seconds 87737960c091b5912dd45beb561a85a9064ea42e616Erik long seconds = (end - start) / DateUtils.SECOND_IN_MILLIS; 87837960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P" + seconds + "S"; 87937960c091b5912dd45beb561a85a9064ea42e616Erik } 88037960c091b5912dd45beb561a85a9064ea42e616Erik } else if (TextUtils.isEmpty(duration)) { 88137960c091b5912dd45beb561a85a9064ea42e616Erik 88237960c091b5912dd45beb561a85a9064ea42e616Erik // If no good duration info exists assume the default 88337960c091b5912dd45beb561a85a9064ea42e616Erik if (isAllDay) { 88437960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P1D"; 88537960c091b5912dd45beb561a85a9064ea42e616Erik } else { 88637960c091b5912dd45beb561a85a9064ea42e616Erik duration = "P3600S"; 88773714169d34a55fc01e14b30897a1ebbacf9934eErik } 88873714169d34a55fc01e14b30897a1ebbacf9934eErik } 88973714169d34a55fc01e14b30897a1ebbacf9934eErik // recurring events should have a duration and dtend set to null 89073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DURATION, duration); 891eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Events.DTEND, (Long) null); 89273714169d34a55fc01e14b30897a1ebbacf9934eErik } 89373714169d34a55fc01e14b30897a1ebbacf9934eErik 89473714169d34a55fc01e14b30897a1ebbacf9934eErik /** 895eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Uses the recurrence selection and the model data to build an rrule and 896eca82e9468c002590045338f98588ad3ce1a0bb3Erik * write it to the model. 89773714169d34a55fc01e14b30897a1ebbacf9934eErik * 89873714169d34a55fc01e14b30897a1ebbacf9934eErik * @param selection the type of rrule 89973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The event to update 9008e3d430a020744faa21bf4ca24f1a99c36ec5c4fMason Tang * @param weekStart the week start day, specified as java.util.Calendar 9018e3d430a020744faa21bf4ca24f1a99c36ec5c4fMason Tang * constants 90273714169d34a55fc01e14b30897a1ebbacf9934eErik */ 9038e3d430a020744faa21bf4ca24f1a99c36ec5c4fMason Tang static void updateRecurrenceRule(int selection, CalendarEventModel model, 9048e3d430a020744faa21bf4ca24f1a99c36ec5c4fMason Tang int weekStart) { 90573714169d34a55fc01e14b30897a1ebbacf9934eErik // Make sure we don't have any leftover data from the previous setting 90673714169d34a55fc01e14b30897a1ebbacf9934eErik EventRecurrence eventRecurrence = new EventRecurrence(); 90773714169d34a55fc01e14b30897a1ebbacf9934eErik 90873714169d34a55fc01e14b30897a1ebbacf9934eErik if (selection == DOES_NOT_REPEAT) { 90973714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = null; 91073714169d34a55fc01e14b30897a1ebbacf9934eErik return; 91173714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_CUSTOM) { 91273714169d34a55fc01e14b30897a1ebbacf9934eErik // Keep custom recurrence as before. 91373714169d34a55fc01e14b30897a1ebbacf9934eErik return; 91473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_DAILY) { 91573714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.DAILY; 91673714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_EVERY_WEEKDAY) { 91773714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.WEEKLY; 91873714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 5; 91973714169d34a55fc01e14b30897a1ebbacf9934eErik int[] byday = new int[dayCount]; 92073714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bydayNum = new int[dayCount]; 92173714169d34a55fc01e14b30897a1ebbacf9934eErik 92273714169d34a55fc01e14b30897a1ebbacf9934eErik byday[0] = EventRecurrence.MO; 92373714169d34a55fc01e14b30897a1ebbacf9934eErik byday[1] = EventRecurrence.TU; 92473714169d34a55fc01e14b30897a1ebbacf9934eErik byday[2] = EventRecurrence.WE; 92573714169d34a55fc01e14b30897a1ebbacf9934eErik byday[3] = EventRecurrence.TH; 92673714169d34a55fc01e14b30897a1ebbacf9934eErik byday[4] = EventRecurrence.FR; 92773714169d34a55fc01e14b30897a1ebbacf9934eErik for (int day = 0; day < dayCount; day++) { 92873714169d34a55fc01e14b30897a1ebbacf9934eErik bydayNum[day] = 0; 92973714169d34a55fc01e14b30897a1ebbacf9934eErik } 93073714169d34a55fc01e14b30897a1ebbacf9934eErik 93173714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = byday; 93273714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = bydayNum; 93373714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = dayCount; 93473714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_WEEKLY_ON_DAY) { 93573714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.WEEKLY; 93673714169d34a55fc01e14b30897a1ebbacf9934eErik int[] days = new int[1]; 93773714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 1; 93873714169d34a55fc01e14b30897a1ebbacf9934eErik int[] dayNum = new int[dayCount]; 93973714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 94073714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 94173714169d34a55fc01e14b30897a1ebbacf9934eErik 94273714169d34a55fc01e14b30897a1ebbacf9934eErik days[0] = EventRecurrence.timeDay2Day(startTime.weekDay); 94373714169d34a55fc01e14b30897a1ebbacf9934eErik // not sure why this needs to be zero, but set it for now. 94473714169d34a55fc01e14b30897a1ebbacf9934eErik dayNum[0] = 0; 94573714169d34a55fc01e14b30897a1ebbacf9934eErik 94673714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = days; 94773714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = dayNum; 94873714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = dayCount; 94973714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_MONTHLY_ON_DAY) { 95073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.MONTHLY; 95173714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = 0; 95273714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthdayCount = 1; 95373714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bymonthday = new int[1]; 95473714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 95573714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 95673714169d34a55fc01e14b30897a1ebbacf9934eErik bymonthday[0] = startTime.monthDay; 95773714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthday = bymonthday; 95873714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_MONTHLY_ON_DAY_COUNT) { 95973714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.MONTHLY; 96073714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayCount = 1; 96173714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bymonthdayCount = 0; 96273714169d34a55fc01e14b30897a1ebbacf9934eErik 96373714169d34a55fc01e14b30897a1ebbacf9934eErik int[] byday = new int[1]; 96473714169d34a55fc01e14b30897a1ebbacf9934eErik int[] bydayNum = new int[1]; 96573714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(model.mTimezone); 96673714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 96773714169d34a55fc01e14b30897a1ebbacf9934eErik // Compute the week number (for example, the "2nd" Monday) 96873714169d34a55fc01e14b30897a1ebbacf9934eErik int dayCount = 1 + ((startTime.monthDay - 1) / 7); 96973714169d34a55fc01e14b30897a1ebbacf9934eErik if (dayCount == 5) { 97073714169d34a55fc01e14b30897a1ebbacf9934eErik dayCount = -1; 97173714169d34a55fc01e14b30897a1ebbacf9934eErik } 97273714169d34a55fc01e14b30897a1ebbacf9934eErik bydayNum[0] = dayCount; 97373714169d34a55fc01e14b30897a1ebbacf9934eErik byday[0] = EventRecurrence.timeDay2Day(startTime.weekDay); 97473714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.byday = byday; 97573714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.bydayNum = bydayNum; 97673714169d34a55fc01e14b30897a1ebbacf9934eErik } else if (selection == REPEATS_YEARLY) { 97773714169d34a55fc01e14b30897a1ebbacf9934eErik eventRecurrence.freq = EventRecurrence.YEARLY; 97873714169d34a55fc01e14b30897a1ebbacf9934eErik } 97973714169d34a55fc01e14b30897a1ebbacf9934eErik 98073714169d34a55fc01e14b30897a1ebbacf9934eErik // Set the week start day. 9818e3d430a020744faa21bf4ca24f1a99c36ec5c4fMason Tang eventRecurrence.wkst = EventRecurrence.calendarDay2Day(weekStart); 98273714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = eventRecurrence.toString(); 98373714169d34a55fc01e14b30897a1ebbacf9934eErik } 98473714169d34a55fc01e14b30897a1ebbacf9934eErik 98573714169d34a55fc01e14b30897a1ebbacf9934eErik /** 986eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Uses an event cursor to fill in the given model This method assumes the 987eca82e9468c002590045338f98588ad3ce1a0bb3Erik * cursor used {@link #EVENT_PROJECTION} as it's query projection. It uses 988eca82e9468c002590045338f98588ad3ce1a0bb3Erik * the cursor to fill in the given model with all the information available. 98973714169d34a55fc01e14b30897a1ebbacf9934eErik * 99073714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The model to fill in 99173714169d34a55fc01e14b30897a1ebbacf9934eErik * @param cursor An event cursor that used {@link #EVENT_PROJECTION} for the query 99273714169d34a55fc01e14b30897a1ebbacf9934eErik */ 99388c483a0189cbe7132330cb1b563f07326f3bcf9Erik public static void setModelFromCursor(CalendarEventModel model, Cursor cursor) { 99473714169d34a55fc01e14b30897a1ebbacf9934eErik if (model == null || cursor == null || cursor.getCount() != 1) { 99573714169d34a55fc01e14b30897a1ebbacf9934eErik Log.wtf(TAG, "Attempted to build non-existent model or from an incorrect query."); 99673714169d34a55fc01e14b30897a1ebbacf9934eErik return; 99773714169d34a55fc01e14b30897a1ebbacf9934eErik } 99873714169d34a55fc01e14b30897a1ebbacf9934eErik 99973714169d34a55fc01e14b30897a1ebbacf9934eErik model.clear(); 100073714169d34a55fc01e14b30897a1ebbacf9934eErik cursor.moveToFirst(); 100173714169d34a55fc01e14b30897a1ebbacf9934eErik 100273714169d34a55fc01e14b30897a1ebbacf9934eErik model.mId = cursor.getInt(EVENT_INDEX_ID); 100373714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTitle = cursor.getString(EVENT_INDEX_TITLE); 100473714169d34a55fc01e14b30897a1ebbacf9934eErik model.mDescription = cursor.getString(EVENT_INDEX_DESCRIPTION); 100573714169d34a55fc01e14b30897a1ebbacf9934eErik model.mLocation = cursor.getString(EVENT_INDEX_EVENT_LOCATION); 100673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mAllDay = cursor.getInt(EVENT_INDEX_ALL_DAY) != 0; 100773714169d34a55fc01e14b30897a1ebbacf9934eErik model.mHasAlarm = cursor.getInt(EVENT_INDEX_HAS_ALARM) != 0; 100873714169d34a55fc01e14b30897a1ebbacf9934eErik model.mCalendarId = cursor.getInt(EVENT_INDEX_CALENDAR_ID); 100973714169d34a55fc01e14b30897a1ebbacf9934eErik model.mStart = cursor.getLong(EVENT_INDEX_DTSTART); 101073714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTimezone = cursor.getString(EVENT_INDEX_TIMEZONE); 101173714169d34a55fc01e14b30897a1ebbacf9934eErik String rRule = cursor.getString(EVENT_INDEX_RRULE); 101273714169d34a55fc01e14b30897a1ebbacf9934eErik model.mRrule = rRule; 101373714169d34a55fc01e14b30897a1ebbacf9934eErik model.mSyncId = cursor.getString(EVENT_INDEX_SYNC_ID); 101473714169d34a55fc01e14b30897a1ebbacf9934eErik model.mTransparency = cursor.getInt(EVENT_INDEX_TRANSPARENCY) != 0; 101573714169d34a55fc01e14b30897a1ebbacf9934eErik int visibility = cursor.getInt(EVENT_INDEX_VISIBILITY); 101673714169d34a55fc01e14b30897a1ebbacf9934eErik model.mOwnerAccount = cursor.getString(EVENT_INDEX_OWNER_ACCOUNT); 101773714169d34a55fc01e14b30897a1ebbacf9934eErik model.mHasAttendeeData = cursor.getInt(EVENT_INDEX_HAS_ATTENDEE_DATA) != 0; 1018eca82e9468c002590045338f98588ad3ce1a0bb3Erik model.mOriginalEvent = cursor.getString(EVENT_INDEX_ORIGINAL_EVENT); 1019352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mOrganizer = cursor.getString(EVENT_INDEX_ORGANIZER); 1020352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mIsOrganizer = model.mOwnerAccount.equalsIgnoreCase(model.mOrganizer); 1021352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mGuestsCanModify = cursor.getInt(EVENT_INDEX_GUESTS_CAN_MODIFY) != 0; 102273714169d34a55fc01e14b30897a1ebbacf9934eErik 102373714169d34a55fc01e14b30897a1ebbacf9934eErik if (visibility > 0) { 1024eca82e9468c002590045338f98588ad3ce1a0bb3Erik // For now the array contains the values 0, 2, and 3. We subtract 1025eca82e9468c002590045338f98588ad3ce1a0bb3Erik // one to make it easier to handle in code as 0,1,2. 102673714169d34a55fc01e14b30897a1ebbacf9934eErik // Default (0), Private (1), Public (2) 102773714169d34a55fc01e14b30897a1ebbacf9934eErik visibility--; 102873714169d34a55fc01e14b30897a1ebbacf9934eErik } 102973714169d34a55fc01e14b30897a1ebbacf9934eErik model.mVisibility = visibility; 103073714169d34a55fc01e14b30897a1ebbacf9934eErik 103173714169d34a55fc01e14b30897a1ebbacf9934eErik boolean hasRRule = !TextUtils.isEmpty(rRule); 103273714169d34a55fc01e14b30897a1ebbacf9934eErik 103373714169d34a55fc01e14b30897a1ebbacf9934eErik // We expect only one of these, so ignore the other 103473714169d34a55fc01e14b30897a1ebbacf9934eErik if (hasRRule) { 103573714169d34a55fc01e14b30897a1ebbacf9934eErik model.mDuration = cursor.getString(EVENT_INDEX_DURATION); 103673714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 103773714169d34a55fc01e14b30897a1ebbacf9934eErik model.mEnd = cursor.getLong(EVENT_INDEX_DTEND); 103873714169d34a55fc01e14b30897a1ebbacf9934eErik } 1039352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1040352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mModelUpdatedWithEventCursor = true; 1041352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1042352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1043352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan /** 1044352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * Uses a calendar cursor to fill in the given model This method assumes the 1045352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * cursor used {@link #CALENDARS_PROJECTION} as it's query projection. It uses 1046352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * the cursor to fill in the given model with all the information available. 1047352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * 1048352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * @param model The model to fill in 1049352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * @param cursor An event cursor that used {@link #CALENDARS_PROJECTION} for the query 1050352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan * @return returns true if model was updated with the info in the cursor. 1051352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan */ 1052352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan public static boolean setModelFromCalendarCursor(CalendarEventModel model, Cursor cursor) { 1053352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (model == null || cursor == null) { 1054352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Log.wtf(TAG, "Attempted to build non-existent model or from an incorrect query."); 1055352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1056352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1057352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1058352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (model.mCalendarId == -1) { 1059352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1060352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1061352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1062352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (!model.mModelUpdatedWithEventCursor) { 1063352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan Log.wtf(TAG, 1064352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan "Can't update model with a Calendar cursor until it has seen an Event cursor."); 1065352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1066352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1067352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1068352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan cursor.moveToPosition(-1); 1069352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan while (cursor.moveToNext()) { 1070ddbf80860566df8425f38a32190e9a64cad4e389Michael Chan if (model.mCalendarId != cursor.getInt(CALENDARS_INDEX_ID)) { 1071352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan continue; 1072352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1073352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1074352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mOrganizerCanRespond = cursor.getInt(CALENDARS_INDEX_ORGANIZER_CAN_RESPOND) != 0; 1075352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1076352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan model.mCalendarAccessLevel = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL); 10779d5f351e45e20ff04da6f9118031cc2cc7419905Michael Chan model.mCalendarDisplayName = cursor.getString(CALENDARS_INDEX_DISPLAY_NAME); 10783400a2bfc1e6be435ea2bb0f436a6ef338446504Erik model.mCalendarColor = cursor.getInt(CALENDARS_INDEX_COLOR); 1079352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1080352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return true; 1081352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1082352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1083352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1084352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1085352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan public static boolean canModifyEvent(CalendarEventModel model) { 1086352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return canModifyCalendar(model) 1087352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan && (model.mIsOrganizer || model.mGuestsCanModify); 1088352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1089352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1090352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan public static boolean canModifyCalendar(CalendarEventModel model) { 1091352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return model.mCalendarAccessLevel >= Calendars.CONTRIBUTOR_ACCESS 1092352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan || model.mCalendarId == -1; 1093352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1094352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 10955f62079ab9daefe95ede87631766e99e8b4a2220Erik public static boolean canAddReminders(CalendarEventModel model) { 10965f62079ab9daefe95ede87631766e99e8b4a2220Erik return model.mCalendarAccessLevel >= Calendars.READ_ACCESS; 10975f62079ab9daefe95ede87631766e99e8b4a2220Erik } 10985f62079ab9daefe95ede87631766e99e8b4a2220Erik 1099352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan public static boolean canRespond(CalendarEventModel model) { 1100352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // For non-organizers, write permission to the calendar is sufficient. 1101352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // For organizers, the user needs a) write permission to the calendar 1102352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // AND b) ownerCanRespond == true AND c) attendee data exist 1103352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // (this means num of attendees > 1, the calendar owner's and others). 1104352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // Note that mAttendeeList omits the organizer. 1105352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1106352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // (there are more cases involved to be 100% accurate, such as 1107352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // paying attention to whether or not an attendee status was 1108352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // included in the feed, but we're currently omitting those corner cases 1109352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // for simplicity). 1110352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1111352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (!canModifyCalendar(model)) { 1112352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1113352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1114352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1115352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (!model.mIsOrganizer) { 1116352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return true; 1117352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1118352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1119352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (!model.mOrganizerCanRespond) { 1120352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1121352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1122352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1123352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // This means we don't have the attendees data so we can't send 1124352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan // the list of attendees and the status back to the server 1125352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan if (model.mHasAttendeeData && model.mAttendeesList.size() == 0) { 1126352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return false; 1127352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan } 1128352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan 1129352e1a2f834460bd54e03ce94cadd36a46debacbMichael Chan return true; 113073714169d34a55fc01e14b30897a1ebbacf9934eErik } 113173714169d34a55fc01e14b30897a1ebbacf9934eErik 113273714169d34a55fc01e14b30897a1ebbacf9934eErik /** 1133eca82e9468c002590045338f98588ad3ce1a0bb3Erik * Goes through an event model and fills in content values for saving This 1134eca82e9468c002590045338f98588ad3ce1a0bb3Erik * method will perform the initial collection of values from the model and 1135eca82e9468c002590045338f98588ad3ce1a0bb3Erik * put them into a set of ContentValues. It performs some basic work such as 1136eca82e9468c002590045338f98588ad3ce1a0bb3Erik * fixing the time on allDay events and choosing whether to use an rrule or 1137eca82e9468c002590045338f98588ad3ce1a0bb3Erik * dtend. 113873714169d34a55fc01e14b30897a1ebbacf9934eErik * 113973714169d34a55fc01e14b30897a1ebbacf9934eErik * @param model The complete model of the event you want to save 114073714169d34a55fc01e14b30897a1ebbacf9934eErik * @return values 114173714169d34a55fc01e14b30897a1ebbacf9934eErik */ 114273714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues getContentValuesFromModel(CalendarEventModel model) { 114373714169d34a55fc01e14b30897a1ebbacf9934eErik String title = model.mTitle; 114473714169d34a55fc01e14b30897a1ebbacf9934eErik boolean isAllDay = model.mAllDay; 114573714169d34a55fc01e14b30897a1ebbacf9934eErik String location = model.mLocation.trim(); 114673714169d34a55fc01e14b30897a1ebbacf9934eErik String description = model.mDescription.trim(); 114773714169d34a55fc01e14b30897a1ebbacf9934eErik String rrule = model.mRrule; 1148eca82e9468c002590045338f98588ad3ce1a0bb3Erik String timezone = model.mTimezone; 114973714169d34a55fc01e14b30897a1ebbacf9934eErik if (timezone == null) { 115073714169d34a55fc01e14b30897a1ebbacf9934eErik timezone = TimeZone.getDefault().getID(); 115173714169d34a55fc01e14b30897a1ebbacf9934eErik } 115273714169d34a55fc01e14b30897a1ebbacf9934eErik Time startTime = new Time(timezone); 115373714169d34a55fc01e14b30897a1ebbacf9934eErik Time endTime = new Time(timezone); 115473714169d34a55fc01e14b30897a1ebbacf9934eErik 115573714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.set(model.mStart); 115673714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.set(model.mEnd); 115773714169d34a55fc01e14b30897a1ebbacf9934eErik 115873714169d34a55fc01e14b30897a1ebbacf9934eErik ContentValues values = new ContentValues(); 115973714169d34a55fc01e14b30897a1ebbacf9934eErik 116073714169d34a55fc01e14b30897a1ebbacf9934eErik long startMillis; 116173714169d34a55fc01e14b30897a1ebbacf9934eErik long endMillis; 116273714169d34a55fc01e14b30897a1ebbacf9934eErik long calendarId = model.mCalendarId; 116373714169d34a55fc01e14b30897a1ebbacf9934eErik if (isAllDay) { 116473714169d34a55fc01e14b30897a1ebbacf9934eErik // Reset start and end time, ensure at least 1 day duration, and set 116573714169d34a55fc01e14b30897a1ebbacf9934eErik // the timezone to UTC, as required for all-day events. 116673714169d34a55fc01e14b30897a1ebbacf9934eErik timezone = Time.TIMEZONE_UTC; 116773714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.hour = 0; 116873714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.minute = 0; 116973714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.second = 0; 117073714169d34a55fc01e14b30897a1ebbacf9934eErik startTime.timezone = timezone; 117173714169d34a55fc01e14b30897a1ebbacf9934eErik startMillis = startTime.normalize(true); 117273714169d34a55fc01e14b30897a1ebbacf9934eErik 117373714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.hour = 0; 117473714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.minute = 0; 117573714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.second = 0; 117673714169d34a55fc01e14b30897a1ebbacf9934eErik endTime.timezone = timezone; 117773714169d34a55fc01e14b30897a1ebbacf9934eErik endMillis = endTime.normalize(true); 117829190975b9238dd6841f822f82f2fb83b0557f36Daisuke Miyakawa if (endMillis < startMillis + DateUtils.DAY_IN_MILLIS) { 117929190975b9238dd6841f822f82f2fb83b0557f36Daisuke Miyakawa // EditEventView#fillModelFromUI() should treat this case, but we want to ensure 118029190975b9238dd6841f822f82f2fb83b0557f36Daisuke Miyakawa // the condition anyway. 118129190975b9238dd6841f822f82f2fb83b0557f36Daisuke Miyakawa endMillis = startMillis + DateUtils.DAY_IN_MILLIS; 118229190975b9238dd6841f822f82f2fb83b0557f36Daisuke Miyakawa } 118373714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 118473714169d34a55fc01e14b30897a1ebbacf9934eErik startMillis = startTime.toMillis(true); 118573714169d34a55fc01e14b30897a1ebbacf9934eErik endMillis = endTime.toMillis(true); 118673714169d34a55fc01e14b30897a1ebbacf9934eErik } 118773714169d34a55fc01e14b30897a1ebbacf9934eErik 118873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.CALENDAR_ID, calendarId); 118973714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.EVENT_TIMEZONE, timezone); 119073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.TITLE, title); 119173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.ALL_DAY, isAllDay ? 1 : 0); 119273714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTSTART, startMillis); 119373714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.RRULE, rrule); 119473714169d34a55fc01e14b30897a1ebbacf9934eErik if (rrule != null) { 119573714169d34a55fc01e14b30897a1ebbacf9934eErik addRecurrenceRule(values, model); 119673714169d34a55fc01e14b30897a1ebbacf9934eErik } else { 1197eca82e9468c002590045338f98588ad3ce1a0bb3Erik values.put(Events.DURATION, (String) null); 119873714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DTEND, endMillis); 119973714169d34a55fc01e14b30897a1ebbacf9934eErik } 120073714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.DESCRIPTION, description); 120173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.EVENT_LOCATION, location); 120273714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.TRANSPARENCY, model.mTransparency ? 1 : 0); 120388c483a0189cbe7132330cb1b563f07326f3bcf9Erik values.put(Events.HAS_ATTENDEE_DATA, model.mHasAttendeeData ? 1 : 0); 120473714169d34a55fc01e14b30897a1ebbacf9934eErik 120573714169d34a55fc01e14b30897a1ebbacf9934eErik int visibility = model.mVisibility; 120673714169d34a55fc01e14b30897a1ebbacf9934eErik if (visibility > 0) { 120773714169d34a55fc01e14b30897a1ebbacf9934eErik // For now the array contains the values 0, 2, and 3. We add one to match. 120873714169d34a55fc01e14b30897a1ebbacf9934eErik // Default (0), Private (2), Public (3) 120973714169d34a55fc01e14b30897a1ebbacf9934eErik visibility++; 121073714169d34a55fc01e14b30897a1ebbacf9934eErik } 121173714169d34a55fc01e14b30897a1ebbacf9934eErik values.put(Events.VISIBILITY, visibility); 121273714169d34a55fc01e14b30897a1ebbacf9934eErik 121373714169d34a55fc01e14b30897a1ebbacf9934eErik return values; 121473714169d34a55fc01e14b30897a1ebbacf9934eErik } 121573714169d34a55fc01e14b30897a1ebbacf9934eErik 121673714169d34a55fc01e14b30897a1ebbacf9934eErik /** 121773714169d34a55fc01e14b30897a1ebbacf9934eErik * Takes an e-mail address and returns the domain (everything after the last @) 121873714169d34a55fc01e14b30897a1ebbacf9934eErik */ 121973714169d34a55fc01e14b30897a1ebbacf9934eErik public static String extractDomain(String email) { 122073714169d34a55fc01e14b30897a1ebbacf9934eErik int separator = email.lastIndexOf('@'); 122173714169d34a55fc01e14b30897a1ebbacf9934eErik if (separator != -1 && ++separator < email.length()) { 122273714169d34a55fc01e14b30897a1ebbacf9934eErik return email.substring(separator); 122373714169d34a55fc01e14b30897a1ebbacf9934eErik } 122473714169d34a55fc01e14b30897a1ebbacf9934eErik return null; 122573714169d34a55fc01e14b30897a1ebbacf9934eErik } 122679f228124de7d98146ca526d743436f6419e2365Erik 122779f228124de7d98146ca526d743436f6419e2365Erik public interface EditDoneRunnable extends Runnable { 122879f228124de7d98146ca526d743436f6419e2365Erik public void setDoneCode(int code); 122979f228124de7d98146ca526d743436f6419e2365Erik } 123073714169d34a55fc01e14b30897a1ebbacf9934eErik} 1231