CalendarProvider2.java revision 74ca9ba319a55a7dcb222344d2582e4dabe5d3bf
19f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/* 29f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 39f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Copyright 2006, The Android Open Source Project 49f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 59f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Licensed under the Apache License, Version 2.0 (the "License"); 69f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** you may not use this file except in compliance with the License. 79f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** You may obtain a copy of the License at 89f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 99f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** http://www.apache.org/licenses/LICENSE-2.0 109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Unless required by applicable law or agreed to in writing, software 129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** distributed under the License is distributed on an "AS IS" BASIS, 139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** See the License for the specific language governing permissions and 149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** limitations under the License. 169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff*/ 179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpackage com.android.providers.calendar; 199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 207be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 217be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 22370f91c0cfe5a5fecaba6120e703f4d2271d2277Erikimport com.google.common.annotations.VisibleForTesting; 23370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.AlarmManager; 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.PendingIntent; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Debug; 44a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 45a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 4606c305d35741db303bd3aacd0eab5af8de0ab34eErikimport android.os.PowerManager; 479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 4806c305d35741db303bd3aacd0eab5af8de0ab34eErikimport android.os.SystemClock; 49f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglioimport android.pim.EventRecurrence; 509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.pim.RecurrenceSet; 519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar; 539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Attendees; 549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.CalendarAlerts; 559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Calendars; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Events; 579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Instances; 589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Reminders; 599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 601edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 61192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 64ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 67ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 70dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.Set; 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 738bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglioimport java.util.concurrent.atomic.AtomicBoolean; 74dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * is defined in {@link android.provider.Calendar}. 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 838bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 857be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 867be45683e367bd6897daf6444b03be938f8f5eaaErik 879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean PROFILE = false; 889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 898f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 908f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff private static final String INVALID_CALENDARALERTS_SELECTOR = 91d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang "_id IN (SELECT ca." + CalendarAlerts._ID + " FROM " 92d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + Tables.CALENDAR_ALERTS + " AS ca" 93d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Tables.INSTANCES 94d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " USING (" + Instances.EVENT_ID + "," 95d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + Instances.BEGIN + "," + Instances.END + ")" 96d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Tables.REMINDERS + " AS r ON" 97d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " (ca." + CalendarAlerts.EVENT_ID + "=r." + Reminders.EVENT_ID 98d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND ca." + CalendarAlerts.MINUTES + "=r." + Reminders.MINUTES + ")" 99d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Views.EVENTS + " AS e ON" 100d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " (ca." + CalendarAlerts.EVENT_ID + "=e." + Events._ID + ")" 101d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " WHERE " + Tables.INSTANCES + "." + Instances.BEGIN + " ISNULL" 102d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR ca." + CalendarAlerts.ALARM_TIME + "<?" 103d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR (r." + Reminders.MINUTES + " ISNULL" 104d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND ca." + CalendarAlerts.MINUTES + "<>0)" 105d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR e." + Calendars.SELECTED + "=0)"; 1068f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 1071ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 1081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 1099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 1119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 1129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 1149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 1159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 1197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_ORIGINAL_EVENT_INDEX = 3; 1207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 129646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 130646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 131646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 132646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 133646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 134646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 135646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 136646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 137646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 138646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 139646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 140646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 141646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 142646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 143646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 144646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 150ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 1539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_URI runs scheduleNextAlarm(false) 15583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_REMOVE_URI runs scheduleNextAlarm(true) 15683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: use a service to schedule alarms rather than private URI 15783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_PATH = "schedule_alarms"; 15883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; 15983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_URI = 16083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_PATH); 16183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_REMOVE_URI = 16283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); 1639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1648bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static final String REMOVE_ALARM_VALUE = "removeAlarms"; 1658bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 1669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // To determine if a recurrence exception originally overlapped the 1679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window, we need to assume a maximum duration, since we only know 1689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original start time. 1699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int MAX_ASSUMED_DURATION = 7*24*60*60*1000; 1709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1718ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 1728ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Due to an issue in Calendar Server restricting the length of the name we had to strip it down 1738ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1748ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1758ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1768ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1778ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1783443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 179c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.EVENT_ID + ", " + 180c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.DTSTART_2445 + ", " + 181c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.DTEND_2445 + ", " + 1823443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1833443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 184b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 185b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1863443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 187b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Calendar.EventsRawTimesColumns.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 188b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 189b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " + 190b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 191b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " SET " + Events._SYNC_DIRTY + "=1" + 192b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 193b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 194b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ID = BaseColumns._ID + "=?"; 195b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EVENT_ID = "event_id=?"; 196b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ORIGINAL_EVENT = Events.ORIGINAL_EVENT + "=?"; 197b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + 199b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID; 200b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 201b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 202b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 203b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID; 204b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 205b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 206b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Views.EVENTS + "." + BaseColumns._ID + "=" + 207b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 208b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 209b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 210b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Views.EVENTS + "." + BaseColumns._ID + "=" + 211b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 212b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 213b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 214b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 215b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 216b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EXTENDED_PROPERTIES + "." + Calendar.ExtendedProperties._ID + "=?"; 217b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 218b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_GET_EVENTS_ENTRIES = 219b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "((" + Events.DTSTART + " <= ? AND " 220b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio + "(" + Events.LAST_DATE + " IS NULL OR " + Events.LAST_DATE + " >= ?)) OR " + 221b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "(" + Events.ORIGINAL_INSTANCE_TIME + " IS NOT NULL AND " + 222b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Events.ORIGINAL_INSTANCE_TIME + " <= ? AND " + 223b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Events.ORIGINAL_INSTANCE_TIME + " >= ?)) AND " + 224b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "(" + Calendars.SYNC_EVENTS + " != 0)"; 225b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 226b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_SELECT_EVENTS_SYNC_ID = 227b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "SELECT " + Events._SYNC_ID + 228b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " FROM " + Tables.EVENTS + 229b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + SQL_WHERE_ID; 230b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 231b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 232b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Calendar.SyncColumns._SYNC_ACCOUNT + "=? AND " + 233b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT_TYPE + "=?"; 234b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 235b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ID_FROM_INSTANCES_NOT_SYNCED = 236b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio BaseColumns._ID + " IN " + 237b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "(SELECT " + Tables.INSTANCES + "." + BaseColumns._ID + " as _id" + 238b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " FROM " + Tables.INSTANCES + 239b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " INNER JOIN " + Tables.EVENTS + 240b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " ON (" + 241b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.INSTANCES + "." + Instances.EVENT_ID + 242b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio ")" + 243b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Tables.EVENTS + "." + Events._ID + "=?)"; 244b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 245b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ID_FROM_INSTANCES_SYNCED = 246b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio BaseColumns._ID + " IN " + 247b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio "(SELECT " + Tables.INSTANCES + "." + BaseColumns._ID + " as _id" + 248b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " FROM " + Tables.INSTANCES + 249b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " INNER JOIN " + Tables.EVENTS + 250b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " ON (" + 251b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.INSTANCES + "." + Instances.EVENT_ID + 252b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio ")" + 253b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Tables.EVENTS + "." + Events._SYNC_ID + "=?" + " OR " + 254b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events.ORIGINAL_EVENT + "=?)"; 255b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 256fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 257fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 258fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2598bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private final AtomicBoolean mNextAlarmCheckScheduled = new AtomicBoolean(false); 2608bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private final AtomicBoolean mNeedRemoveAlarms = new AtomicBoolean(false); 2618bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 2628bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String ACTION_CHECK_NEXT_ALARM = 2638bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio "com.android.providers.calendar.intent.CalendarProvider2"; 264e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private static final int ALARM_CHECK_DELAY_MILLIS = 5000; 2658bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 266b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio public static final class InstancesList extends ArrayList<ContentValues> { 2679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 269b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio public static final class EventInstancesMap extends HashMap<String, InstancesList> { 2701030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff public void add(String syncIdKey, ContentValues values) { 2711030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList instances = get(syncIdKey); 2729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (instances == null) { 2739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances = new InstancesList(); 2741030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff put(syncIdKey, instances); 2759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances.add(values); 2779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We search backward in time for event reminders that we may have missed 2829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and schedule them if the event has not yet expired. The amount in 2839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the past to search backwards is controlled by this constant. It 2849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should be at least a few minutes to allow for an event that was 2859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * recently created on the web to make its way to the phone. Two hours 2869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * might seem like overkill, but it is useful in the case where the user 2879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * just crossed into a new timezone and might have just missed an alarm. 2889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2891edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff private static final long SCHEDULE_ALARM_SLACK = 2 * DateUtils.HOUR_IN_MILLIS; 2909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Alarms older than this threshold will be deleted from the CalendarAlerts 2939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. This should be at least a day because if the timezone is 2949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * wrong and the user corrects it we might delete good alarms that 2959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * appear to be old because the device time was incorrectly in the future. 2969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This threshold must also be larger than SCHEDULE_ALARM_SLACK. We add 2979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the SCHEDULE_ALARM_SLACK to ensure this. 2989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 2999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * To make it easier to find and debug problems with missed reminders, 3009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * set this to something greater than a day. 3019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long CLEAR_OLD_ALARM_THRESHOLD = 3031edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff 7 * DateUtils.DAY_IN_MILLIS + SCHEDULE_ALARM_SLACK; 3049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3058bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio // A lock for synchronizing access to the AlarmManager 3069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Object mAlarmLock = new Object(); 3079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 3099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 3109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 3119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 3129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 3149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 3159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 31781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 31881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 31981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 32081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 32181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + Calendar.Instances.EVENT_ID + "=" + 32281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 32381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + Calendar.Events._ID + ")"; 32481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 32518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 32618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 32718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 32818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 32918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 33018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Instances.EVENT_ID + "=" + 33118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 33218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Events._ID + ")" + ") LEFT OUTER JOIN " + 33318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 33418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 33518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Attendees.EVENT_ID + "=" + 33618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 33718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Events._ID + ")"; 33818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 339b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 34081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.START_DAY + "<=? AND " + 34181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.END_DAY + ">=?"; 34281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 343b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 34481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.BEGIN + "<=? AND " + 34581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.END + ">=?"; 3469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 3489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 3499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 3509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 3519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 3529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 35481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * A regex for describing how we split search queries into tokens. 355dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Keeps quoted phrases as one token. 356dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 357dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * "one \"two three\"" ==> ["one" "two three"] 358dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 359dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 360dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 361dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 362dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 363dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 364dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 365dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 366dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 367dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 36881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 369dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 370dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 371dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 372dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 373dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 374dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 375dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 376dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 377dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 37881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 37918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 38018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 38118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 38218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 38318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 38418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang "group_concat(" + Calendar.Attendees.ATTENDEE_EMAIL + ")"; 38518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 38618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 38718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 38818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 38918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 39018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 39118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang "group_concat(" + Calendar.Attendees.ATTENDEE_NAME + ")"; 39218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 39381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 39481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Events.TITLE, 39581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Events.DESCRIPTION, 39618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang Calendar.Events.EVENT_LOCATION, 39718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 39818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 39981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 40081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 401e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private CalendarAlarmManager mAlarmManager; 4029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 403a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 404a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 405a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 406a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 407a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 408a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 409a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 410a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 411a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 412a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 413a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 414a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 415dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 416dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 417dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 418dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 419dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 420dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 421e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 422e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 4238bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 4248bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4258bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private volatile PowerManager.WakeLock mScheduleNextAlarmWakeLock; 4268bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static final String SCHEDULE_NEXT_ALARM_WAKE_LOCK = "ScheduleNextAlarmWakeLock"; 427a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 428a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 429a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 430a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 431dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 432a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 433a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 434a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 435dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 436dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 437dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 438dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 439dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 440a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 441a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 442a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 443a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 4579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 4619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void verifyAccounts() { 4699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 4709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff onAccountsUpdated(AccountManager.get(getContext()).getAccounts()); 4719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4798bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4808bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4818bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4828bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 483e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio PowerManager.WakeLock getScheduleNextAlarmWakeLock() { 484e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mScheduleNextAlarmWakeLock == null) { 485e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 486e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio // Create a wake lock that will be used when we are actually scheduling the next alarm 487e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mScheduleNextAlarmWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 488e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio SCHEDULE_NEXT_ALARM_WAKE_LOCK); 489e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio // We want the Wake Lock to be reference counted (so that we dont need to take care 490e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio // about its reference counting) 491e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mScheduleNextAlarmWakeLock.setReferenceCounted(true); 492e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 493e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio return mScheduleNextAlarmWakeLock; 494e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 495e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 496e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio void acquireScheduleNextAlarmWakeLock() { 497e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getScheduleNextAlarmWakeLock().acquire(); 4988bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4998bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 500e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio void releaseScheduleNextAlarmWakeLock() { 501e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getScheduleNextAlarmWakeLock().release(); 502e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 503e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 504e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 505e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 506e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 507e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 508e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 509e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 510e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 5118bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 5128bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 5159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 516ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 517ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 518ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 519f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 520f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 521f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 522ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 523ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 524ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 526ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 5278bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 5288bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 529dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 530e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 531e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 532ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 533ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 5369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 5379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 5399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 5419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 5439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 5449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 5459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 546e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 5479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData = new MetaData(mDbHelper); 549ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 550ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 551e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager(); 5529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 553e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getScheduleNextAlarmWakeLock(); 554e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 555e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 5568bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 5589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 560e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio protected CalendarAlarmManager createCalendarAlarmManager() { 561e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio return new CalendarAlarmManager(mContext); 562e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 563e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 564e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 565e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mAlarmManager == null) { 566e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mAlarmManager = createCalendarAlarmManager(); 567e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 568e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio return mAlarmManager; 569e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 570e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 571ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 572ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 573ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 574ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 575ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 576ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 577ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 578ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 579ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 580ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 581ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 582ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 583ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 584ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 585ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 586ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 5879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 590315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 5919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 5939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 5959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 5989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 6009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 601ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 6029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 606315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 607315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 608315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 609315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 610315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 611315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 612315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 613315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 6149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 6159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 6169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 617315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 618ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 619315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 620315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 621a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 622315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 623315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 624315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 625ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 626315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 627315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 628ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 629315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 630ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 631ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 632ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 633ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 634ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio rescheduleMissedAlarms(); 635ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 636ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 637f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 638f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 639f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 640ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 641ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 642ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 643ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 644ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 645f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 646f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 647f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 648ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 6499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 650ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 651ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 652315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 653ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 654ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 6553443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 656ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 657315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 660ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 661ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 662ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 663ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 664ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 6653443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 6663443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 667ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 668ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 669ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 671ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6723443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 673f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 674f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 675f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 676f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 677f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 678f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 679f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 680ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6813443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 682ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 683ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 684ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 685ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 686ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 687ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 688ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 690ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 691ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 692ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 693f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 694f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 695f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 696ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 697ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 698ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 699ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 700ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 701ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 702f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 703f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 704f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 705ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 706ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 707ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 708ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 709ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 710ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 711ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 712ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 713ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 714b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 715b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 716ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 717b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 718dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 719ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 720ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 721ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 722ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 723ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 724ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 725ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 726ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 727ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 728ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 729ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 730f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 731f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 732f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 733ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 734ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 7359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 736ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 737ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 738ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 739ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 740315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 741315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 742ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 743ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 744ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 745ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 746ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 74725e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 748ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 749315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 750315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 751ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 752ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 753f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 754f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 755f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 756ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 757ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 758ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 759315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 760315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 761315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 762315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 763315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 764ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 7659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 7669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 7679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 7689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 769315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 770315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7761f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7791f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7801f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7811f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7821f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7831f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7841f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 785d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio null /* selection */, null /* sort */, 786d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 787315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 788315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 789315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone()); 7901f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 7911f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 7921f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 7931f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7941f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void rescheduleMissedAlarms() { 800e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().rescheduleMissedAlarms(mContentResolver); 8019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 804b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 807e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver.notifyChange(Calendar.CONTENT_URI, null, syncToNetwork); 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 813ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 814ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 8189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 8209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 8219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 822315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 8239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 8269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 8289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 8299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 8311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 833595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 8349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 8361ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 838636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 839b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 84119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 84219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 84319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 84419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 845595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 84619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 84719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 84819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 84919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 850636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 851b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 85219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 85319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 8549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 85543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 856b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 857595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 8589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 86043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 861b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 862636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 863b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 8669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 8699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 8719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 881315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 8829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return handleInstanceQuery(qb, begin, end, projection, 883d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio selection, sortOrder, match == INSTANCES_BY_DAY, 884315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 885315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 88681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 88781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 88881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 88981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 89081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 89181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 89281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 89381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 89481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 89581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 89681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 89781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 89881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 89981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 900315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 90181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 90281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 90381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return handleInstanceSearchQuery(qb, begin, end, query, projection, 904315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio selection, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 905315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9066db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 9099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 9119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 9149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 9179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 9199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 921315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 922315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 923315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 925b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS); 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 927b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(Tables.EVENTS + "." + Events._ID + "=" 928b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio + Tables.ATTENDEES + "." + Attendees.EVENT_ID); 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 931b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS); 9329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 933636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 934b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 937b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 940b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS); 9419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 942636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 943b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 9449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 946b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 948b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 951b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 953b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 957b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 959636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 960b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 9619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 963b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 966b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 967636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 968b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 9699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 970315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 971b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 972315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 973315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 9809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 9839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 9849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 985ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 986ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 987ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 988ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 989ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 990ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 991ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 992ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 993ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 9949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 9959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 9969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 9979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 998e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio c.setNotificationUri(mContentResolver, Calendar.Events.CONTENT_URI); 9999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 10049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 10069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 10079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 10089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 10099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 10109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 10139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 1014d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1015315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1016315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 10179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 10189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 1020d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio long rangeEnd, String[] projection, String selection, String sort, 1021315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean searchByDay, boolean forceExpansion, String instancesTimezone, 1022315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 10239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 102481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 10259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 10269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 10279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 10289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 1029315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 10309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 10319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 10329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 10339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 10349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1035315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 1036315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1037b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1040315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 1041315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1042b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 10439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10448335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(rangeEnd), 10458335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 10468335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 10477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 10489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 105081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 1051dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1052dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1053dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1054dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1055dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1056dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1057dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1058dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1059dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1060dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1061dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 106281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1063dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1064dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 106581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 106681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 106781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 106881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 106981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 107081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1071dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1072dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1073dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1074dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1075dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1076dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1077dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1078dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1079dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1080dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1081dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1082dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1083dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1084dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 108581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 108681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 108781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 108881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 108981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 109081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 109181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 109281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 109381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 109481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 109581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 109681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 109781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 109881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 109981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 110081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 110181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 110281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 110381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 110481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * approximation of full-text search across multiple columns. 110581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 110681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 110781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 110881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 110981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 111081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 111281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 111381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 111481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 111581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 111681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1117dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1118dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1119dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 112081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 112181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 112281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 112381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 112418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 112518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 112618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 112718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 112881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 112981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 113081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 113181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 113281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 113381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 113418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 113518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 113681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 113718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 113818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 113918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 114081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1141f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1142f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 114318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 114481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 114581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 114681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 114781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 114881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 114981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 115081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 1151315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String selection, String sort, boolean searchByDay, String instancesTimezone, 1152315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 115318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 115481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1156dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 115781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] selectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 115818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 115918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1160dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1161dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 116281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 116381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 116481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1165315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 116681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 116781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 116881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 116981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 117081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 117118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 117218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 117356292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 117456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1175315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1176315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1177315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 117856292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1179b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 118081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 118181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 118218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 118318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 118456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 118556292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1186315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1187315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1188315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 118956292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1190b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 119181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 119318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 119418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang Instances._ID /* groupBy */, searchWhere /* having */, sort); 119581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 11976db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1198315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1199315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 120081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 12016db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 120243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 120343556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1204315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1205192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 120643556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 120743556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1208192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 120943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1210315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1211315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1212b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 12138335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 12148335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 12158335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 12166db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 12179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. Acquires the database lock and calls {@link #acquireInstanceRangeLocked}. 12229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1226d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1227315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1228315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1230d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1231315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1232315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 12339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 12349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1235315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1236315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 12379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 12389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 12399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 12409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 12469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1250315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1251315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1252315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1254315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1255315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 12569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 12579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 12589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1259315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1260315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1261315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 12649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 12659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 12669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 12679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 12689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 12719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 12799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 12809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 12819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1282315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1285315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1286315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1287315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 12897be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 12907be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 12917be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 12927be45683e367bd6897daf6444b03be938f8f5eaaErik } 1293315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1294315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1295315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1296d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1298b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1299f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13006db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 13029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1303315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1304315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1305315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1307315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 13087be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 13097be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 13107be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 13117be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 13127be45683e367bd6897daf6444b03be938f8f5eaaErik 13137be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1314315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 13157be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 13167be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 13177be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 13187be45683e367bd6897daf6444b03be938f8f5eaaErik } 1319315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1335f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1345315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1352315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 13549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1357315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 13589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EXPAND_COLUMNS = new String[] { 13619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._ID, 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.STATUS, 13649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTSTART, 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTEND, 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EVENT_TIMEZONE, 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 13689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 13699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXRULE, 13709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXDATE, 13719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DURATION, 13729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ALL_DAY, 13739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 13741030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff Events.ORIGINAL_INSTANCE_TIME, 13751dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio Events.CALENDAR_ID, 13761b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio Events.DELETED 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 13789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make instances for the given range. 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void expandInstanceRangeLocked(long begin, long end, String localTimezone) { 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.startMethodTracing("expandInstanceRangeLocked"); 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Expanding events between " + begin + " and " + end); 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getEntries(begin, end); 13939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff performInstanceExpansion(begin, end, localTimezone, entries); 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 13979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 13989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 14019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.stopMethodTracing(); 14029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 14069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Get all entries affecting the given window. 14079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 14089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 14099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return Cursor for the entries; caller must close it. 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getEntries(long begin, long end) { 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 14131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String beginString = String.valueOf(begin); 14179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String endString = String.valueOf(end); 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // grab recurrence exceptions that fall outside our expansion window but modify 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences that do fall within our window. we won't insert these into the output 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set of instances, but instead will just add them to our cancellations list, so we 14229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // can cancel the correct recurrence expansion instances. 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we don't have originalInstanceDuration or end time. for now, assume the original 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance lasts no longer than 1 week. 14252d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio // also filter with syncable state (we dont want the entries from a non syncable account) 14269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: compute the originalInstanceEndTime or get this from the server. 1427b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_GET_EVENTS_ENTRIES); 14288335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {endString, beginString, endString, 14298335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(begin - MAX_ASSUMED_DURATION)}; 1430e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Cursor c = qb.query(mDb, EXPAND_COLUMNS, null /* selection */, 14318335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff selectionArgs, null /* groupBy */, 14327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 1433e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 1434e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.v(TAG, "Instance expansion: got " + c.getCount() + " entries"); 1435e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1436e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff return c; 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 14401030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * Generates a unique key from the syncId and calendarId. 14411030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * The purpose of this is to prevent collisions if two different calendars use the 14421030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * same sync id. This can happen if a Google calendar is accessed by two different accounts, 14431030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * or with Exchange, where ids are not unique between calendars. 14441030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param syncId Id for the event 14451030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param calendarId Id for the calendar 14461030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @return key 14471030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff */ 14481030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff private String getSyncIdKey(String syncId, long calendarId) { 14491030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff return calendarId + ":" + syncId; 14501030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff } 14511030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 14521030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff /** 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Perform instance expansion on the given entries. 14549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 14559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 14569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param localTimezone 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param entries The entries to process. 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void performInstanceExpansion(long begin, long end, String localTimezone, 14609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries) { 14619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 14629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14631030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // Key into the instance values to hold the original event concatenated with calendar id. 14641030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff final String ORIGINAL_EVENT_AND_CALENDAR = "ORIGINAL_EVENT_AND_CALENDAR"; 14651030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int statusColumn = entries.getColumnIndex(Events.STATUS); 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtstartColumn = entries.getColumnIndex(Events.DTSTART); 14689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtendColumn = entries.getColumnIndex(Events.DTEND); 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int eventTimezoneColumn = entries.getColumnIndex(Events.EVENT_TIMEZONE); 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int durationColumn = entries.getColumnIndex(Events.DURATION); 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rruleColumn = entries.getColumnIndex(Events.RRULE); 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rdateColumn = entries.getColumnIndex(Events.RDATE); 14739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exruleColumn = entries.getColumnIndex(Events.EXRULE); 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exdateColumn = entries.getColumnIndex(Events.EXDATE); 14759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int allDayColumn = entries.getColumnIndex(Events.ALL_DAY); 14769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int idColumn = entries.getColumnIndex(Events._ID); 14779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int syncIdColumn = entries.getColumnIndex(Events._SYNC_ID); 14789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalEventColumn = entries.getColumnIndex(Events.ORIGINAL_EVENT); 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalInstanceTimeColumn = entries.getColumnIndex(Events.ORIGINAL_INSTANCE_TIME); 14801030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff int calendarIdColumn = entries.getColumnIndex(Events.CALENDAR_ID); 14811b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio int deletedColumn = entries.getColumnIndex(Events.DELETED); 14829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues initialValues; 14849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff EventInstancesMap instancesMap = new EventInstancesMap(); 14859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 14879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time eventTime = new Time(); 14889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: entries contains all events that affect the current 14909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window. It consists of: 14919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. These will be 14929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed. 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Recurrences that included the window. These will be displayed 14949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if not canceled. 14959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Recurrence exceptions that fall in the window. These will be 14969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed if not cancellations. 14979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 14989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window (subject to 1 week assumption above), but are outside 14999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the window. These will not be displayed. Cases c and d are 15009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // distingushed by the start / end time. 15019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (entries.moveToNext()) { 15039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 15049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = null; 15059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = entries.getInt(allDayColumn) != 0; 15079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String eventTimezone = entries.getString(eventTimezoneColumn); 15099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(eventTimezone)) { 15109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the events table, allDay events start at midnight. 15119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this forces them to stay at midnight for all day events 15129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: check that this actually does the right thing. 15139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTimezone = Time.TIMEZONE_UTC; 15149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = entries.getLong(dtstartColumn); 15179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long eventId = Long.valueOf(entries.getLong(idColumn)); 15189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = entries.getString(durationColumn); 15209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 15219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 15229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 15239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff catch (DateException e) { 1525f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1526f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "error parsing duration for event " 1527f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + eventId + "'" + durationStr + "'", e); 1528f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 15299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 15309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 15319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 15329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 15339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 15349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 15359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 15369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String syncId = entries.getString(syncIdColumn); 15409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = entries.getString(originalEventColumn); 15419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalInstanceTimeMillis = -1; 15439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(originalInstanceTimeColumn)) { 15449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis= entries.getLong(originalInstanceTimeColumn); 15459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = entries.getInt(statusColumn); 15471dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio boolean deleted = (entries.getInt(deletedColumn) != 0); 15489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rruleStr = entries.getString(rruleColumn); 15509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rdateStr = entries.getString(rdateColumn); 15519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exruleStr = entries.getString(exruleColumn); 15529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exdateStr = entries.getString(exdateColumn); 15531030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff long calendarId = entries.getLong(calendarIdColumn); 15541030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String syncIdKey = getSyncIdKey(syncId, calendarId); // key into instancesMap 15559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1556f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 1557f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 1558f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(rruleStr, rdateStr, exruleStr, exdateStr); 1559f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 1560f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1561f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + rruleStr, e); 1562f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 1563f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio continue; 1564f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 15659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1566f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 15679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating 15689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (status == Events.STATUS_CANCELED) { 15709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen! 1571f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1572f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Found canceled recurring event in " 1573f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "Events table. Ignoring."); 1574f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 15759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 15769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1577370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik if (deleted) { 157806c61055fc7a1504eefcd6c9a471f7370526e532Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 157906c61055fc7a1504eefcd6c9a471f7370526e532Fabrice Di Meglio Log.d(TAG, "Found deleted recurring event in " 158006c61055fc7a1504eefcd6c9a471f7370526e532Fabrice Di Meglio + "Events table. Ignoring."); 158106c61055fc7a1504eefcd6c9a471f7370526e532Fabrice Di Meglio } 1582370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik continue; 1583370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik } 15849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // need to parse the event into a local calendar. 15869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = eventTimezone; 15879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.set(dtstartMillis); 15889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.allDay = allDay; 15899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 15919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen. 1592f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1593f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Repeating event has no duration -- " 1594f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "should not happen."); 1595f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 15969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 15979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set to one day. 15989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 15999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 16009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 1; 16019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 16029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 16039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 16049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P1D"; 16059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // compute the duration from dtend, if we can. 16079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // otherwise, use 0s. 16089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 16099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 16109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 16119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 16129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 16139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 16149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = entries.getLong(dtendColumn); 16159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = (int) ((dtendMillis - dtstartMillis) / 1000); 16169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P" + duration.seconds + "S"; 16179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 16199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 16209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long[] dates; 16259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dates = rp.expand(eventTime, recur, begin, end); 16269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Initialize the "eventTime" timezone outside the loop. 16289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is used in computeTimezoneDependentFields(). 16299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 16309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 16319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 16339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long durationMillis = duration.getMillis(); 16369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long date : dates) { 16379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 16389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 16399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.BEGIN, date); 16419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = date + durationMillis; 16429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 16439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(date, dtendMillis, 16459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 16461030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 16479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating 16509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 16519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if this event has an "original" field, then record 16539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that we need to cancel the original event (we can't 16549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do that here because the order of this loop isn't 16559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // defined) 16569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 16571030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // The ORIGINAL_EVENT_AND_CALENDAR holds the 16581030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // calendar id concatenated with the ORIGINAL_EVENT to form 16591030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // a unique key, matching the keys for instancesMap. 16601030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff initialValues.put(ORIGINAL_EVENT_AND_CALENDAR, 16611030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff getSyncIdKey(originalEvent, calendarId)); 16629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.ORIGINAL_INSTANCE_TIME, 16639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis); 16649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, status); 16659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = dtstartMillis; 16689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 16699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 16709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = entries.getLong(dtendColumn); 16719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = duration.addTo(dtstartMillis); 16749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this non-recurring event might be a recurrence exception that doesn't 16779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // actually fall within our expansion window, but instead was selected 16789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so we can correctly cancel expanded recurrence instances below. do not 16799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // add events to the instances map if they don't actually fall within our 16809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansion window. 16819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((dtendMillis < begin) || (dtstartMillis > end)) { 16829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 16839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, Events.STATUS_CANCELED); 16849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 1685f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1686f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Unexpected event outside window: " + syncId); 1687f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 16889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 16899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 16939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16941dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio initialValues.put(Instances.BEGIN, dtstartMillis); 16959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 16969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16971dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // we temporarily store the DELETED status (will be cleaned later) 16981b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio initialValues.put(Events.DELETED, deleted); 16991dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 17009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 17019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 17029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 17039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 17049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, 17069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 17079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17081030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 17099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 1711f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1712f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 1713f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 17149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (TimeFormatException e) { 1715f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1716f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 1717f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 17189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains all instances that affect the 17221030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // window, indexed by original sync id concatenated with calendar id. 17231030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // It consists of: 17249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. They have: 17259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 17269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window. They may 17279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be subject to exceptions. They have: 17289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 17299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They have: 17301030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS (since they can 17319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be a modification or cancellation), EVENT_ID, BEGIN, END 17329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 17339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. They have: 17341030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS = 17359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED, EVENT_ID, BEGIN, END 17369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // First, delete the original instances corresponding to recurrence 17389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exceptions. We do this by iterating over the list and for each 17399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrence exception, we search the list for an instance with a 17409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // matching "original instance time". If we find such an instance, 17419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we remove it from the list. If we don't find such an instance 17429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then we cancel the recurrence exception. 17439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Set<String> keys = instancesMap.keySet(); 17441030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 17451030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 17469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 17479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If this instance is not a recurrence exception, then 17499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // skip it. 17501030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff if (!values.containsKey(ORIGINAL_EVENT_AND_CALENDAR)) { 17519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 17529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17541030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String originalEventPlusCalendar = values.getAsString(ORIGINAL_EVENT_AND_CALENDAR); 17559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 17561030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList originalList = instancesMap.get(originalEventPlusCalendar); 17579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalList == null) { 17589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The original recurrence is not present, so don't try canceling it. 17599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 17609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Search the original event for a matching original 17639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance time. If there is a matching one, then remove 17649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original one. We do this both for exceptions that 17659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // change the original instance as well as for exceptions 17669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that delete the original instance. 17679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (int num = originalList.size() - 1; num >= 0; num--) { 17689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues originalValues = originalList.get(num); 17699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginTime = originalValues.getAsLong(Instances.BEGIN); 17709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (beginTime == originalTime) { 17719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We found the original instance, so remove it. 17729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalList.remove(num); 17739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains filtered instances. 17799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // It consists of: 17809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. 17819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window and have not 17829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // been subject to exceptions. 17839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They will have 17849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED if they are cancellations. 17859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 17869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. These are STATUS_CANCELED. 17879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do the inserts. Since the db lock is held when this method is executed, 17899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this will be done in a transaction. 17909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if there is lock contention (e.g., a sync is trying to merge into the db 17919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // while the calendar app is trying to query the db (expanding instances)), we will 17929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // not be "polite" and yield the lock until we're done. This will favor local query 17939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // operations over sync/write operations. 17941030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 17951030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 17969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 17979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17981dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // If this instance was cancelled or deleted then don't create a new 17999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance. 18009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer status = values.getAsInteger(Events.STATUS); 18011b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio boolean deleted = values.containsKey(Events.DELETED) ? 18021b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.getAsBoolean(Events.DELETED) : false; 18031dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio if ((status != null && status == Events.STATUS_CANCELED) || deleted) { 18049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 18059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18071dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // We remove this useless key (not valid in the context of Instances table) 18081b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.remove(Events.DELETED); 18091dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 18109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Remove these fields before inserting a new instance 18111030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff values.remove(ORIGINAL_EVENT_AND_CALENDAR); 18129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 18139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.STATUS); 18149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1815c874ed5c6cc0fcc6ac06ae7d20db0eab7d749608Ken Shirriff mDbHelper.instancesReplace(values); 18169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 18219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Computes the timezone-dependent fields of an instance of an event and 18229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * updates the "values" map to contain those fields. 18239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 18249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin the start time of the instance (in UTC milliseconds) 18259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end the end time of the instance (in UTC milliseconds) 18269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param local a Time object with the timezone set to the local timezone 18279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values a map that will contain the timezone-dependent fields 18289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 18299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void computeTimezoneDependentFields(long begin, long end, 18309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local, ContentValues values) { 18319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(begin); 18329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay = Time.getJulianDay(begin, local.gmtoff); 18339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startMinute = local.hour * 60 + local.minute; 18349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(end); 18369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay = Time.getJulianDay(end, local.gmtoff); 18379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endMinute = local.hour * 60 + local.minute; 18389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Special case for midnight, which has endMinute == 0. Change 18409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that to +24 hours on the previous day to make everything simpler. 18419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Exception: if start and end minute are both 0 on the same day, 18429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then leave endMinute alone. 18439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (endMinute == 0 && endDay > startDay) { 18449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endMinute = 24 * 60; 18459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay -= 1; 18469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_DAY, startDay); 18499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_DAY, endDay); 18509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_MINUTE, startMinute); 18519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_MINUTE, endMinute); 18529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 18559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 18569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 18579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 18589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 18599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 18609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 18619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 18629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 18639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 18649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 18659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 18669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 18679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 18689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 18699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 18709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 18719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 18729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 18739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 18746db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 18759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 187648587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 187748587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1878315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1879315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 18809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 18819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 18829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1885fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio public static boolean isRecurrenceEvent(String rrule, String rdate, String originalEvent) { 1886fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (!TextUtils.isEmpty(rrule)|| 1887fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio !TextUtils.isEmpty(rdate)|| 1888fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio !TextUtils.isEmpty(originalEvent)); 18899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1891646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1892646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1893646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1894646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1895646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * corrects the fields DTSTART, DTEND, and DURATION if necessary. Also checks to ensure that 1896646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * either both DTSTART and DTEND or DTSTART and DURATION are set for each event. 1897646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1898646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @param updatedValues The values to check and correct 1899646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1900646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1901646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private boolean fixAllDayTime(Uri uri, ContentValues updatedValues) { 1902646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1903646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (updatedValues.containsKey(Events.ALL_DAY) 1904646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik && updatedValues.getAsInteger(Events.ALL_DAY).intValue() == 1) { 1905646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtstart = updatedValues.getAsLong(Events.DTSTART); 1906646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtend = updatedValues.getAsLong(Events.DTEND); 1907646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String duration = updatedValues.getAsString(Events.DURATION); 1908646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Time time = new Time(); 1909646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Cursor currentTimesCursor = null; 1910646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String tempValue; 1911646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If a complete set of time fields doesn't exist query the db for them. A complete set 1912646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // is dtstart and dtend for non-recurring events or dtstart and duration for recurring 1913646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // events. 1914646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if(dtstart == null || (dtend == null && duration == null)) { 1915646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we have an id to search for, if not this is probably a new event 1916646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 2) { 1917646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = query(uri, 1918646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik ALLDAY_TIME_PROJECTION, 1919646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selection */, 1920646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selectionArgs */, 1921646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* sort */); 1922646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1923646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (!currentTimesCursor.moveToFirst() || 1924646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.getCount() != 1) { 1925646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Either this is a new event or the query is too general to get data 1926646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // from the db. In either case don't try to use the query and catch 1927646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // errors when trying to update the time fields. 1928646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1929646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = null; 1930646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1931646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1932646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1933646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1934646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1935646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Ensure dtstart exists for this event (always required) and set so h,m,s are 0 if 1936646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // necessary. 1937646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // TODO Move this somewhere to check all events, not just allDay events. 1938646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtstart == null) { 1939646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1940646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields, we'd like to know if a field is empty 1941646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1942646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTSTART_INDEX); 1943646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1944646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtstart = Long.valueOf(tempValue); 1945646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1946646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1947646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("Event has no DTSTART field, the db " + 1948646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "may be damaged. Set DTSTART for this event to fix."); 1949646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1950646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 1951646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTSTART cannot be empty for new events."); 1952646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1953646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1954646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1955646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtstart.longValue()); 1956646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1957646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1958646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1959646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1960646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTSTART, time.toMillis(true)); 1961646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1962646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1963646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1964646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If dtend exists for this event make sure it's h,m,s are 0. 1965646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend == null && currentTimesCursor != null) { 1966646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields. We'd like to know if a field is empty 1967646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1968646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTEND_INDEX); 1969646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1970646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = Long.valueOf(tempValue); 1971646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1972646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = null; 1973646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1974646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1975646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend != null) { 1976646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1977646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtend.longValue()); 1978646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1979646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1980646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1981646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1982646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = time.toMillis(true); 1983646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTEND, dtend); 1984646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1985646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1986646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1987646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1988646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1989646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null) { 1990646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = currentTimesCursor.getString(ALLDAY_DURATION_INDEX); 1991646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1992646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1993646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1994646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1995646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration != null) { 1996646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int len = duration.length(); 1997646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1998646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * in the seconds format, and if so converts it to days. 1999646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 2000646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (len == 0) { 2001646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = null; 2002646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else if (duration.charAt(0) == 'P' && 2003646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration.charAt(len - 1) == 'S') { 2004646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int seconds = Integer.parseInt(duration.substring(1, len - 1)); 2005646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 2006646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = "P" + days + "D"; 2007646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DURATION, duration); 2008646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 2009646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2010646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2011646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 2012646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null && dtend == null) { 2013646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 2014646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "an event."); 2015646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2016646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2017646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 2018646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2019646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 20209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2021b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 2022ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 20239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 20249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 20279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2028b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final int match = sUriMatcher.match(uri); 20299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 20309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 20319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 20329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 20347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 20367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 20389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 20399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 2041e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 2042e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff validateEventData(updatedValues); 2043e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 2044e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 20459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 20479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 20489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 20509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 20519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 20529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 20539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 20549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 20559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 20579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 20589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 20599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2061646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, updatedValues)) { 2062f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2063f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 2064f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 2065f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2066646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2067c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 20719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, true /* new event */, mDb); 20729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 20749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 20759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 20769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 20779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 20789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 20819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20828ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // if the Event Timezone is defined, store it as the original one in the 20838ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // ExtendedProperties table 20848ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) { 20858ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE); 20868ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20878ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues expropsValues = new ContentValues(); 20888ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.EVENT_ID, id); 20898ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.NAME, 20908ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio EXT_PROP_ORIGINAL_TIMEZONE); 20918ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.VALUE, originalTimezone); 20928ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20938ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Insert the extended property 20948ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues); 20958ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (exPropId == -1) { 20968ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20978ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot add the original Timezone in the " 20988ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + "ExtendedProperties table for Event: " + id); 20998ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 21008ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } else { 21018ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Update the Event for saying it has some extended properties 21028ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues eventValues = new ContentValues(); 21038ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1"); 2104b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update("Events", eventValues, SQL_WHERE_ID, 21058ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio new String[] {String.valueOf(id)}); 21068ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (result <= 0) { 21078ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 21088ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot update hasExtendedProperties column" 21098ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + " for Event: " + id); 21108ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 21118ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 21128ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 21138ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 2114dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountName = values.getAsString(Calendars._SYNC_ACCOUNT); 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 21229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars._SYNC_ACCOUNT_TYPE); 21239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 21241b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio String eventsUrl = values.getAsString(Calendars.SYNC1); 21251b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 21269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2128dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 21319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 21329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 21339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.attendeesInsert(values); 21367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 21377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Attendees.EVENT_ID)); 21387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 21449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 21459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 21469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.remindersInsert(values); 21497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 21507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Reminders.EVENT_ID)); 21517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 21549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 21559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 21569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 21589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 21609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 21619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 21629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 21652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 21662fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 21679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 21699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Calendar.ExtendedProperties.EVENT_ID)) { 21709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 21719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.extendedPropertiesInsert(values); 21747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 21757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Calendar.ExtendedProperties.EVENT_ID)); 21767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 21799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 21809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 21819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 21829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 21839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 21849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 21856db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2186315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 21877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 21889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 21899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 21909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 21939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 21949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 21979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2199e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2200e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Do some validation on event data before inserting. 2201e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * In particular make sure dtend, duration, etc make sense for 2202e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * the type of event (regular, recurrence, exception). Remove 2203e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * any unexpected fields. 2204e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 2205e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues to insert 2206e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2207e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private void validateEventData(ContentValues values) { 2208e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2209e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2210e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2211e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2212e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT)); 2213e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2214e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2215e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2216e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2217e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2218e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2219e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is the recurrence rule 2220e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2221e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2222e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2223e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 2224e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2225e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence: " + values); 2226e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2227e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2228e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_EVENT); 2229e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 2230e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2231e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2232e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2233e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2234e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2235e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2236e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2237e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2238e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2239e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2240e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 2241e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2242e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence exception: " + values); 2243e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2244e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2245e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2246e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2247e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2248e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2249e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2250e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2251e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2252e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2253e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2254e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2255e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 2256e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2257e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for event: " + values); 2258e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2259e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2260e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2261e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2262e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2263e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 22647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private void setEventDirty(int eventId) { 2265b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Integer[] {eventId}); 22667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 22689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 22709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2274f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2275f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2276f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2277f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2278f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2279f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 22809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 22819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 22829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 22839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 22869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 22879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 22889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2290f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2291f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2292f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 22999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 23099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 23109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 23119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 23129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 23139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 23149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 23159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 23169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 23189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 23199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 23209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 23219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 23229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 23239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 23269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 23279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 23339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 23429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 23439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 23449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 23469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 23479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 23489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 23499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 23509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 23519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2352f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2353f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2354f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 23589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 23599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 23609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 23619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 23659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 23669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 23679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 23689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 23699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 23709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 23719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 23729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 23739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2374f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2375f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2376f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 23819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 23829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 23839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 23919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 23929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 23939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 23949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 23979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 23989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 23999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 24009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 24019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 24059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 24069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 24079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 24089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 24099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 24139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 24149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 24179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2418b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2419b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 24209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the instances table when an event is added or updated. 24249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 24259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 24269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param newEvent true if the event is new. 24279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 24289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateInstancesLocked(ContentValues values, 24309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 24319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean newEvent, 24329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 24339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If there are no expanded Instances, then return. 24359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 24369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (fields.maxInstance == 0) { 24379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 24389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 24419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis == null) { 24429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (newEvent) { 24439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // must be present for a new event. 24449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART missing."); 24459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2446f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 2447f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Missing DTSTART. No need to update instance."); 2448f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 24499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 24509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 24549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!newEvent) { 24569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Want to do this for regular event, recurrence, or exception. 24579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // For recurrence or exception, more deletion may happen below if we 24589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do an instance expansion. This deletion will suffice if the exception 24599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // is moved outside the window, for instance. 2460b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.delete(Tables.INSTANCES, Instances.EVENT_ID + "=?", 2461b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(rowId)}); 24629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2464fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String rrule = values.getAsString(Events.RRULE); 2465fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String rdate = values.getAsString(Events.RDATE); 2466fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 2467fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isRecurrenceEvent(rrule, rdate, originalEvent)) { 24689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The recurrence or exception needs to be (re-)expanded if: 24699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Exception or recurrence that falls inside window 24709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean insideWindow = dtstartMillis <= fields.maxInstance && 24719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff (lastDateMillis == null || lastDateMillis >= fields.minInstance); 24729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Exception that affects instance inside window 24739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // These conditions match the query in getEntries 24749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // See getEntries comment for explanation of subtracting 1 week. 24759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean affectsWindow = originalInstanceTime != null && 24769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime <= fields.maxInstance && 24779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime >= fields.minInstance - MAX_ASSUMED_DURATION; 24789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (insideWindow || affectsWindow) { 24799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateRecurrenceInstancesLocked(values, rowId, db); 24809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: an exception creation or update could be optimized by 24829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // updating just the affected instances, instead of regenerating 24839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the recurrence. 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 24889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis == null) { 24899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = dtstartMillis; 24909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if the event is in the expanded range, insert 24939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // into the instances table. 24949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: deal with durations. currently, durations are only used in 24959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences. 24969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis <= fields.maxInstance && dtendMillis >= fields.minInstance) { 24989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues instanceValues = new ContentValues(); 24999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.EVENT_ID, rowId); 25009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.BEGIN, dtstartMillis); 25019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.END, dtendMillis); 25029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 25049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 25059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 25079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the timezone-dependent fields. 25109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local = new Time(); 25119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 25129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = Time.TIMEZONE_UTC; 25139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = fields.timezone; 25159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, local, instanceValues); 25189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.instancesInsert(instanceValues); 25199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 25239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Determines the recurrence entries associated with a particular recurrence. 25249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This set is the base recurrence and any exception. 25259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 25269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Normally the entries are indicated by the sync id of the base recurrence 25279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * (which is the originalEvent in the exceptions). 25289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * However, a complication is that a recurrence may not yet have a sync id. 25299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * In that case, the recurrence is specified by the rowId. 25309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 25319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param recurrenceSyncId The sync id of the base recurrence, or null. 25329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The row id of the base recurrence. 25339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return the relevant entries. 25349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 25359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getRelevantRecurrenceEntries(String recurrenceSyncId, long rowId) { 25369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 25379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 2540636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String selectionArgs[]; 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 2542b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio String where = SQL_WHERE_ID; 25439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2544636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {String.valueOf(rowId)}; 25459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 2546b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio String where = Events._SYNC_ID + "=? OR " + Events.ORIGINAL_EVENT + "=?"; 25479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2548636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {recurrenceSyncId, recurrenceSyncId}; 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 25519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Retrieving events to expand: " + qb.toString()); 25529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2554636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return qb.query(mDb, EXPAND_COLUMNS, null /* selection */, selectionArgs, 25557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* groupBy */, null /* having */, null /* sortOrder */); 25569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 25599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Do incremental Instances update of a recurrence or recurrence exception. 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method does performInstanceExpansion on just the modified recurrence, 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid the overhead of recomputing the entire instance table. 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 25649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 25659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 25669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 25679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 25689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateRecurrenceInstancesLocked(ContentValues values, 25699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 25709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 25719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 2572315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 25739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 2574315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String recurrenceSyncId; 25759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null) { 25769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = originalEvent; 25779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the recurrence's sync id from the database 2579b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio recurrenceSyncId = DatabaseUtils.stringForQuery(db, 2580b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_SELECT_EVENTS_SYNC_ID, new String[] {String.valueOf(rowId)}); 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrenceSyncId is the _sync_id of the underlying recurrence 25839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the recurrence hasn't gone to the server, it will be null. 25849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to clear out old instances 25869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 25879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating updating a recurrence that hasn't gone to the server. 25889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to delete based on row id 2589b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio String where = SQL_WHERE_ID_FROM_INSTANCES_NOT_SYNCED; 2590b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.delete(Tables.INSTANCES, where, new String[]{"" + rowId}); 25919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating or modifying a recurrence or exception. 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Delete instances for recurrence (_sync_id = recurrenceSyncId) 25949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // and all exceptions (originalEvent = recurrenceSyncId) 2595b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio String where = SQL_WHERE_ID_FROM_INSTANCES_SYNCED; 2596b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.delete(Tables.INSTANCES, where, new String[]{recurrenceSyncId, recurrenceSyncId}); 25979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do instance expansion 26009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getRelevantRecurrenceEntries(recurrenceSyncId, rowId); 26019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 2602315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio performInstanceExpansion(fields.minInstance, fields.maxInstance, instancesTimezone, 26039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries); 26049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 26059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 26069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 26079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 26129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 26139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 26149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 26159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 26169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 26179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 26189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 26199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 26209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 26219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 26229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 26239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 26259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 26279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 26289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 26309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 26319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 26329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 26339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 26349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 26369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 26379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 26389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2645f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2646f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2647f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2648f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2649f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2650f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2651f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio values.get(Calendar.Events.RRULE), e); 2652f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2653f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2654f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 26559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2656f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 26579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 26589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 26599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 26619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 26639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 26649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 26659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 26679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 26699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 26719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 26729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 26739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 26749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 26769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 26779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 26789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 26849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2686e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2687e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 2688e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues (in/out) 2689e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2690e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2691e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 26929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2701f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2702f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2703f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 27099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2711b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio rawValues.put(Calendar.EventsRawTimes.EVENT_ID, eventId); 27129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 27149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 2731b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio rawValues.put(Calendar.EventsRawTimesColumns.DTSTART_2445, time.format2445()); 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 27369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 2737b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio rawValues.put(Calendar.EventsRawTimesColumns.DTEND_2445, time.format2445()); 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 27439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 27449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 27459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 27469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 27479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 2750b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio rawValues.put(Calendar.EventsRawTimesColumns.ORIGINAL_INSTANCE_TIME_2445, 2751b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 27569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 27579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 2758b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio rawValues.put(Calendar.EventsRawTimesColumns.LAST_DATE_2445, time.format2445()); 27599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 27629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2765b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2766b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2767ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 27689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 27699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 27719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 27729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 27739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 27749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 2776dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 27779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 27789323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2779dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2780dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2781dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2782dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 27839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 27871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection = appendAccountToSelection(uri, selection); 27887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 27891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 2790b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, ID_ONLY_PROJECTION, 27911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 27927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 27939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 27951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 279610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 27979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 279810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 2799dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 28009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 28019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 28029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 28039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 28059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28061ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 28071ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 28081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 28091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 28101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 28111ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 28121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 28131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 281410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 28151ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 28169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 28179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2819b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 28207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2821b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, selection, selectionArgs); 28227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 28259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28272fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28282fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2831b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 2832b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 28337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2834b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, null /* selection */, 28352fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2841b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, selection, selectionArgs); 28427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2843b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, selection, selectionArgs); 28447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2853b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, SQL_WHERE_ID, 2854b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 28557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2856b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, null /* selection */, 28572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 28612fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 28622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 2863b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 28642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2865b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, selection, 2866b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 28672fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 28702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 28712fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28722fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28732fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28742fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 28752fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2876b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 2877636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 28782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2879b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, null /* selection */, 28802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28817e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 28849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2886b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 28877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2888b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.CALENDAR_ALERTS, uri, selection, selectionArgs); 28897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 28929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28962fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 28972fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 28989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2899b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 2900b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 29037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot delete that URL: " + uri); 29049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 2905b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio StringBuilder selectionSb = new StringBuilder(BaseColumns._ID + "="); 29069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 29079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 29089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 29099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 29109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 29119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 29139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 29149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2915595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 291674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 29179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 29189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 29196db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2920315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 29219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 29229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 29239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 29249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 292710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 29281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2929192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 29301ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 2932b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 2933b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 2934636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 29351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 29361ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 29371ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 29381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 29391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 294048f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 29411ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29421ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 29431ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 29441ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 29451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 29461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 29471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String origEvent = cursor.getString(EVENTS_ORIGINAL_EVENT_INDEX); 2948fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isRecurrenceEvent(rrule, rdate, origEvent)) { 29491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 29501ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 295248f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 295348f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 295448f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 2955b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 29561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 29571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 29581b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 29591ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 2960b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 296102494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 296243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 296343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 2964b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 2965b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 2966b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 2967b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 2968b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 2969b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 29701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 29731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 29741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 29751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29768f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 297710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 297810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 2979dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 298010b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 29811ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 29821ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29831ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 29857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 29867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 29877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 29887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 29897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 29907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 29917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 29927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 29937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 29947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 29957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues values = new ContentValues(); 29967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, "1"); 29977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 29987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 29997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 30007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 30017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 3002b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(table, SQL_WHERE_ID, new String[] {String.valueOf(id)}); 3003b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 3004b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 30057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 30067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 30087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 30097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 30117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 30137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 30147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 30157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 30167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 30177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 30187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 30197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 30207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 30217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 30227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 30237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 30247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 30257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 30267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues dirtyValues = new ContentValues(); 30277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff dirtyValues.put(Events._SYNC_DIRTY, "1"); 30287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 30297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 30307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 30317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 30327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 3033b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(table, values, SQL_WHERE_ID, new String[] {String.valueOf(id)}); 3034b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 3035b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 30367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 30377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 30397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 30407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 30427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 304474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 30459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 30469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 30479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 304874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 304974ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 305074ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 305174ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 305274ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 30539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 30549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 30559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 30579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 30589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 30599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 30609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 30629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 30639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 306474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 30659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3067fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private Cursor getCursorForEventIdAndProjection(String eventId, String[] projection) { 3068fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return mDb.query(Tables.EVENTS, 3069fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio projection, 3070fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio SQL_WHERE_ID, 3071fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { eventId }, 3072fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* group by */, 3073fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* having */, 3074fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* order by*/); 3075fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3076fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3077fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 3078fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 3079fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3080fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 3081fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3082fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3083fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3084fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 3085fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 3086fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 3087fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3088fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3089fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 3090fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 3091fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3092fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 3093fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 3094fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 3095fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3096fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 3097fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 3098fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 3099fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesStatusCancelUpdateMeanUpdate(String eventId, ContentValues values) { 3100fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio boolean isStatusCanceled = values.containsKey(Events.STATUS) && 3101fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 3102fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 3103fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Cursor cursor = null; 3104fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio try { 3105fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio cursor = getCursorForEventIdAndProjection(eventId, 3106fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { Events.RRULE, Events.RDATE, Events.ORIGINAL_EVENT }); 3107fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (!cursor.moveToFirst()) { 3108fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3109fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "Cannot find Event with id: " + eventId); 3110fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3111fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3112fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3113fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String rrule = cursor.getString(0); 3114fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String rdate = cursor.getString(1); 3115fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String originalEvent = cursor.getString(2); 3116fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3117fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio boolean isRecurrenceException = 3118fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio isRecurrenceEvent(rrule, rdate, originalEvent) && 3119fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio !TextUtils.isEmpty(originalEvent); 3120fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3121fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isRecurrenceException) { 3122fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return doesEventExistForSyncId(originalEvent); 3123fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3124fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } finally { 3125fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio cursor.close(); 3126fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3127fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3128fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is the normal case, we just want an UPDATE 3129fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return true; 3130fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3131fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 31329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: call calculateLastDate()! 31339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 31349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 3135b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 3136ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 31379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 31389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 31419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 31429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 314443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS 3145315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio && match != EVENTS && match != CALENDARS && match != PROVIDER_PROPERTIES) { 3146b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio throw new IllegalArgumentException("WHERE based updates not supported"); 31479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3148fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 31499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 31509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 31519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 31529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 31539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 31559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 3156dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 3157dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 31589323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3159dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3160dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3161dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 31629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 316443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 31659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 31669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 316743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 316843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 316943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null) { 317043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio throw new UnsupportedOperationException("Selection not permitted for " 317143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio + uri); 317243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 317343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 317443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 317543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 317643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 317743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 317843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 317943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 318043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null && selection.startsWith("_id=")) { 318143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 318243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 318343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 318443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 3185b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 318643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 318743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 318843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 318943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio values.put(Calendars._SYNC_DIRTY, 1); 31902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 31929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 31939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 31949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3196b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 3197636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31993ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3200d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 3201d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang if (values.containsKey(Calendars.SELECTED)) { 3202d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3203d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3204d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3205d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3206d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang scheduleNextAlarm(false); 3207d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 32083ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3209dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 32103ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 32113ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 32129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 32139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 32159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 32169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 32177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = 0; 32187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (match == EVENTS_ID) { 32197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = ContentUris.parseId(uri); 3220a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else if (callerIsSyncAdapter) { 322143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: same remark as for CALENDARS/CALENDARS_ID case as this is not 322243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // sufficient to deal with all the "_id" case in selection 3223a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff if (selection != null && selection.startsWith("_id=")) { 32247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // The ContentProviderOperation generates an _id=n string instead of 32257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // adding the id to the URL, so parse that out here. 32267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = Long.parseLong(selection.substring(4)); 3227a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else { 3228a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // Sync adapter Events operation affects just Events table, not associated 3229a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // tables. 3230646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, values)) { 3231f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3232f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: Caller is sync adapter. " + 3233f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3234f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3235646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3236a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff return mDb.update("Events", values, selection, selectionArgs); 3237a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } 32387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3239a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 32407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 32427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 32437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Disallow updating the attendee status in the Events 32459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // table. In the future, we could support this but we 32469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // would have to query and update the attendees table 32479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to keep the values consistent. 32489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 32499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 32509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.SELF_ATTENDEE_STATUS 32519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 32529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // TODO: should we allow this? 32557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (values.containsKey(Events.HTML_URI) && !callerIsSyncAdapter) { 32569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 32579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.HTML_URI 32589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 32599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3260fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String strId = String.valueOf(id); 3261fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // For taking care about recurrences exceptions cancelations, check if this needs 3262fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // to be an UPDATE or a DELETE 3263fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio boolean isUpdate = doesStatusCancelUpdateMeanUpdate(strId, values); 3264e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 3265e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // TODO: should extend validateEventData to work with updates and call it here 3266e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 32679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 3268f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3269f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not update event."); 3270f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 32719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 32729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3273646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we pass in a uri with the id appended to fixAllDayTime 3274646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Uri allDayUri; 3275646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 1) { 3276646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = ContentUris.withAppendedId(uri, id); 3277646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 3278646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = uri; 3279646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3280646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(allDayUri, updatedValues)) { 3281f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3282f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: " + 3283f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3284f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3285646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 32869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3287fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio int result; 32889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3289fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isUpdate) { 3290fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio result = mDb.update(Tables.EVENTS, updatedValues, SQL_WHERE_ID, 3291fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { strId }); 3292fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (result > 0) { 3293fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio updateEventRawTimesLocked(id, updatedValues); 3294fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio updateInstancesLocked(updatedValues, id, false /* not a new event */, mDb); 3295fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 329606c305d35741db303bd3aacd0eab5af8de0ab34eErik if (values.containsKey(Events.DTSTART) || 329706c305d35741db303bd3aacd0eab5af8de0ab34eErik values.containsKey(Events.STATUS)) { 329806c305d35741db303bd3aacd0eab5af8de0ab34eErik // If this is a cancellation knock it out 329906c305d35741db303bd3aacd0eab5af8de0ab34eErik // of the instances table 330006c305d35741db303bd3aacd0eab5af8de0ab34eErik if (values.containsKey(Events.STATUS) && 330106c305d35741db303bd3aacd0eab5af8de0ab34eErik values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 330206c305d35741db303bd3aacd0eab5af8de0ab34eErik String[] args = new String[] {String.valueOf(id)}; 330306c305d35741db303bd3aacd0eab5af8de0ab34eErik mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 330406c305d35741db303bd3aacd0eab5af8de0ab34eErik } 330506c305d35741db303bd3aacd0eab5af8de0ab34eErik 3306fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // The start time of the event changed, so run the 3307fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // event alarm scheduler. 3308fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 3309fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.d(TAG, "updateInternal() changing event"); 3310fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3311fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 33129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33133ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3314fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio sendUpdateNotification(id, callerIsSyncAdapter); 3315fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3316fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } else { 3317fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio result = deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3318fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 3319fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio sendUpdateNotification(callerIsSyncAdapter); 33209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3321fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 33229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 33239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 33252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 33262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 33272fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 33289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 33299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 33309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 33327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3333b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.ATTENDEES, values, SQL_WHERE_ID, 333483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 33357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3336b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.ATTENDEES, values, uri, null /* selection */, 33372fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 33387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33402fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 33412fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 33422fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 33432fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 33442fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 33452fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 33469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3347b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 3348636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 33499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 33512fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 33522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 3353b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 33549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 33562fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 33572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 33582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 33597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 33607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3361b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = mDb.update(Tables.REMINDERS, values, SQL_WHERE_ID, 336283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 33637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3364b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = updateInTable(Tables.REMINDERS, values, uri, null /* selection */, 33652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 33667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 33689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 33699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 33709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 33719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 33729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 33747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 33759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33762fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 33772fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 33782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 33792fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 33807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 33817e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3382b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.EXTENDED_PROPERTIES, values, SQL_WHERE_ID, 3383636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 33847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3385b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.EXTENDED_PROPERTIES, values, uri, 3386b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio null /* selection */, null /* selectionArgs */); 33877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 338983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 339083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 339183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 339283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(false); 339383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 339483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 339583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 339683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(true); 339783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 339883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 33999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3400315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 3401315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (selection == null) { 3402315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection cannot be null for " + uri); 3403315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3404315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 3405315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 3406315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3407315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3408315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 3409315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3410315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 3411315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 3412315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 3413315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3414315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3415315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 3416315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 3417315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3418315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 3419315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 3420b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 3421315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3422315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 3423315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone type is set to "home", set the Instances timezone to the previous 3424315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone type is set to "auto", set the Instances timezone to the current 3425315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // device one 3426315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone Instances is set AND if we are in "home" timezone type, then 3427315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // save the timezone Instance into "previous" too 3428315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 3429315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 3430315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 3431315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 3432315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 3433315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 3434315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 3435315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 3436315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 3437315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 3438315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 3439315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3440315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 3441d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 3442315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 3443315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3444d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3445315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3446315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3447315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 3448315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 3449315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 3450315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 3451315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 3452315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3453d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3454315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3455315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3456315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3457315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3458315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 3459315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 3460315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 3461315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 3462315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 3463315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 3464315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 3465315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 3466d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 3467315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 3468315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 3469315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3470d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3471315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3472315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3473315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3474315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3475315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 3476315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3477315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 34789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 34799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 34809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3483595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) { 3484b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3485b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_NAME); 3486b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3487b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_TYPE); 3488595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 3489595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere(Calendar.Calendars._SYNC_ACCOUNT + "=" 3490595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3491595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 3492595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 3493595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 3494595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere("1"); // I.e. always true 3495595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3496595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3497595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 34989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 3499b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3500b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_NAME); 3501b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3502b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_TYPE); 35039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 35049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder(Calendar.Calendars._SYNC_ACCOUNT + "=" 35059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 35069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 35079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 35089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 35099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 35109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 35119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 35129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selectionSb.toString(); 35149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 35159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 35169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 35209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 35219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 35229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 35239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] {Calendars._SYNC_ACCOUNT, Calendars._SYNC_ACCOUNT_TYPE, 35241b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio Calendars.SYNC1, Calendars.SYNC_EVENTS}, 35259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 35269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 35279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 35289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 35309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 35319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 3532ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 35339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3534ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 3535ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 3536ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 3537ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 3538ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 3539ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 3540ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 35419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 35429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 35439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35469535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 35479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 3548f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3549f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 3550f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 3551f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 35529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 35539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35559535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 35569535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 35579535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 35589535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 35599535627bf6295cd94447beb83e1aac41f50c3600Erik } 35609535627bf6295cd94447beb83e1aac41f50c3600Erik 35619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 35629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 35639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 35649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 35679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 35689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 35699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarmCheck(long triggerTime) { 35729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Intent intent = new Intent(CalendarReceiver.SCHEDULE); 3573e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio intent.setClass(mContext, CalendarReceiver.class); 3574e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio PendingIntent pending = PendingIntent.getBroadcast(mContext, 35759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_NO_CREATE); 35769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (pending != null) { 35779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Cancel any previous alarms that do the same thing. 3578e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().cancel(pending); 35799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3580e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio pending = PendingIntent.getBroadcast(mContext, 35819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 35829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 35849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 35859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(triggerTime); 35869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 35879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "scheduleNextAlarmCheck at: " + triggerTime + timeStr); 35889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3590e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().set(AlarmManager.RTC_WAKEUP, triggerTime, pending); 35919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarm(boolean removeAlarms) { 35948bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio // We aggregate first the "remove alarm flag". Whenever it is to true, it will be sticky 35958bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mNeedRemoveAlarms.set(mNeedRemoveAlarms.get() || removeAlarms); 35968bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio if (!mNextAlarmCheckScheduled.getAndSet(true)) { 35978bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 35988bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio Log.d(TAG, "Scheduling check of next Alarm"); 35998bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 36008bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio Intent intent = new Intent(ACTION_CHECK_NEXT_ALARM); 36018bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio intent.putExtra(REMOVE_ALARM_VALUE, removeAlarms); 36028bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio PendingIntent pending = PendingIntent.getBroadcast(mContext, 36038bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 0 /* ignored */, intent, PendingIntent.FLAG_NO_CREATE); 36048bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio if (pending != null) { 36058bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio // Cancel any previous Alarm check requests 3606e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().cancel(pending); 36078bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 36088bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio pending = PendingIntent.getBroadcast(mContext, 36098bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 0 /* ignored */, intent, PendingIntent.FLAG_CANCEL_CURRENT); 36108bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 36118bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio // Trigger the check in 5s from now 3612e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio long triggerAtTime = SystemClock.elapsedRealtime() + ALARM_CHECK_DELAY_MILLIS; 3613e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3614e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio triggerAtTime, pending); 36158bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 36169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 36199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread and schedules an alarm for 36209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the next calendar event, if necessary. 36219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 36228bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio void runScheduleNextAlarm(boolean removeAlarms) { 36238bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio // Reset so that we can accept other schedules of next alarm 36248bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mNextAlarmCheckScheduled.set(false); 3625e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb.beginTransaction(); 36269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 36279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (removeAlarms) { 3628e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio removeScheduledAlarmsLocked(mDb); 36299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3630e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio scheduleNextAlarmLocked(mDb); 3631e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb.setTransactionSuccessful(); 36329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 3633e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb.endTransaction(); 36349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 36389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method looks at the 24-hour window from now for any events that it 36399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * needs to schedule. This method runs within a database transaction. It 36409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * also runs in a background thread. 36419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 36429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The CalendarProvider2 keeps track of which alarms it has already scheduled 36439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid scheduling them more than once and for debugging problems with 36449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms. It stores this knowledge in a database table called CalendarAlerts 36459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * which persists across reboots. But the actual alarm list is in memory 36469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and disappears if the phone loses power. To avoid missing an alarm, we 36479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * clear the entries in the CalendarAlerts table when we start up the 36489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * CalendarProvider2. 36499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 36509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Scheduling an alarm multiple times is not tragic -- we filter out the 36519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * extra ones when we receive them. But we still need to keep track of the 36529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled alarms. The main reason is that we need to prevent multiple 36539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * notifications for the same alarm (on the receive side) in case we 36549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * accidentally schedule the same alarm multiple times. We don't have 36559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * visibility into the system's alarm list so we can never know for sure if 36569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we have already scheduled an alarm and it's better to err on scheduling 36579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * an alarm twice rather than missing an alarm. Another reason we keep 36589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * track of scheduled alarms in a database table is that it makes it easy to 36599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * run an SQL query to find the next reminder that we haven't scheduled. 36609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 36619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 36629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 36639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void scheduleNextAlarmLocked(SQLiteDatabase db) { 3664b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang Time time = new Time(); 36659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long currentMillis = System.currentTimeMillis(); 36679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long start = currentMillis - SCHEDULE_ALARM_SLACK; 36689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long end = start + (24 * 60 * 60 * 1000); 36699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 36709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(start); 36719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 36729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "runScheduleNextAlarm() start search: " + startTimeStr); 36739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36758f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Delete rows in CalendarAlert where the corresponding Instance or 36768f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Reminder no longer exist. 36778f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Also clear old alarms but keep alarms around for a while to prevent 36789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // multiple alerts for the same reminder. The "clearUpToTime' 36799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should be further in the past than the point in time where 36809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we start searching for events (the "start" variable defined above). 36818f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff String selectArg[] = new String[] { 36828f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Long.toString(currentMillis - CLEAR_OLD_ALARM_THRESHOLD) 36838f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff }; 36848f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 36858f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff int rowsDeleted = 36868f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff db.delete(CalendarAlerts.TABLE_NAME, INVALID_CALENDARALERTS_SELECTOR, selectArg); 36879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long nextAlarmTime = end; 3689e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio final long tmpAlarmTime = CalendarAlerts.findNextAlarmTime(mContentResolver, currentMillis); 36908f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (tmpAlarmTime != -1 && tmpAlarmTime < nextAlarmTime) { 36918f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff nextAlarmTime = tmpAlarmTime; 36929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Extract events from the database sorted by alarm time. The 36959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // alarm times are computed from Instances.begin (whose units 36969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // are milliseconds) and Reminders.minutes (whose units are 36979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // minutes). 36989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 36999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events whose end time is already in the past. 37009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events alarms that we have already scheduled. 37019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 37029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 1: we can add support for the case where Reminders.minutes 37039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // equals -1 to mean use Calendars.minutes by adding a UNION for 37049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that case where the two halves restrict the WHERE clause on 37059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders.minutes != -1 and Reminders.minutes = 1, respectively. 37069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 37079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 2: we have to name "myAlarmTime" different from the 37089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "alarmTime" column in CalendarAlerts because otherwise the 37099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query won't find multiple alarms for the same event. 3710156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // 3711156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // The CAST is needed in the query because otherwise the expression 3712156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will be untyped and sqlite3's manifest typing will not convert the 3713156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // string query parameter to an int in myAlarmtime>=?, so the comparison 3714156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will fail. This could be simplified if bug 2464440 is resolved. 3715b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3716b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang time.setToNow(); 3717b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang time.normalize(false); 3718b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang long localOffset = time.gmtoff * 1000; 3719b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3720b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String allDayOffset = " -(" + localOffset + ") "; 3721b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String subQueryPrefix = "SELECT " + Instances.BEGIN; 3722b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String subQuerySuffix = " -(" + Reminders.MINUTES + "*" + 3723b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + DateUtils.MINUTE_IN_MILLIS + ")" 3724b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AS myAlarmTime" + "," + Tables.INSTANCES 3725d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "." + Instances.EVENT_ID + " AS eventId" 3726d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Instances.BEGIN + "," + Instances.END 3727d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Instances.TITLE + "," + Instances.ALL_DAY 3728d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Reminders.METHOD + "," + Reminders.MINUTES 3729d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " FROM " + Tables.INSTANCES 3730d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " INNER JOIN " + Views.EVENTS 3731d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ON (" + Views.EVENTS + "." + Events._ID 3732d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "=" + Tables.INSTANCES + "." + Instances.EVENT_ID + ")" 3733d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " INNER JOIN " + Tables.REMINDERS 3734d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ON (" + Tables.INSTANCES + "." + Instances.EVENT_ID 3735d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + ")" 3736d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " WHERE " + Calendars.SELECTED + "=1" 3737156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime>=CAST(? AS INT)" 3738156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime<=CAST(? AS INT)" 3739d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND " + Instances.END + ">=?" 3740b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Reminders.METHOD + "=" + Reminders.METHOD_ALERT; 3741b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3742b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we query separately for all day events to convert to local time from UTC 3743b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we need to /subtract/ the offset to get the correct resulting local time 3744b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String allDayQuery = subQueryPrefix + allDayOffset + subQuerySuffix 3745b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Instances.ALL_DAY + "=1"; 3746b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String nonAllDayQuery = subQueryPrefix + subQuerySuffix 3747b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Instances.ALL_DAY + "=0"; 3748b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3749b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we use UNION ALL because we are guaranteed to have no dupes between 3750b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // the two queries, and it is less expensive 3751b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String query = "SELECT *" 3752b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " FROM (" + allDayQuery + " UNION ALL " + nonAllDayQuery + ")" 3753b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // avoid rescheduling existing alarms 3754b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio + " WHERE 0=(SELECT count(*) FROM " + Tables.CALENDAR_ALERTS + " CA" 3755b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " WHERE CA." + CalendarAlerts.EVENT_ID + "=eventId" 3756b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND CA." + CalendarAlerts.BEGIN + "=" + Instances.BEGIN 3757b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND CA." + CalendarAlerts.ALARM_TIME + "=myAlarmTime)" 3758d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ORDER BY myAlarmTime," + Instances.BEGIN + "," + Instances.TITLE; 37599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3760b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String queryParams[] = new String[] { String.valueOf(start), String.valueOf(nextAlarmTime), 3761b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String.valueOf(currentMillis), String.valueOf(start), String.valueOf(nextAlarmTime), 3762b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String.valueOf(currentMillis) }; 3763b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3764315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 3765315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone = mCalendarCache.readTimezoneType().equals( 3766315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.TIMEZONE_TYPE_HOME); 3767b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // expand this range by a day on either end to account for all day events 3768b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang acquireInstanceRangeLocked(start - DateUtils.DAY_IN_MILLIS, 3769b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang end + DateUtils.DAY_IN_MILLIS, 3770d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* don't use minimum expansion windows */, 3771315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 3772315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 3773315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone); 37749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 37759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3776156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff cursor = db.rawQuery(query, queryParams); 37779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37788f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int beginIndex = cursor.getColumnIndex(Instances.BEGIN); 37798f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int endIndex = cursor.getColumnIndex(Instances.END); 37808f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int eventIdIndex = cursor.getColumnIndex("eventId"); 37818f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int alarmTimeIndex = cursor.getColumnIndex("myAlarmTime"); 37828f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutesIndex = cursor.getColumnIndex(Reminders.MINUTES); 37839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 37859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(nextAlarmTime); 37869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String alarmTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 37878f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "cursor results: " + cursor.getCount() + " nextAlarmTime: " 37888f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + alarmTimeStr); 37899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (cursor.moveToNext()) { 37929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule all alarms whose alarm time is as early as any 37939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // scheduled alarm. For example, if the earliest alarm is at 37949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 1pm, then we will schedule all alarms that occur at 1pm 37959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // but no alarms that occur later than 1pm. 37969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Actually, we allow alarms up to a minute later to also 37979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be scheduled so that we don't have to check immediately 37989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // again after an event alarm goes off. 37998f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long alarmTime = cursor.getLong(alarmTimeIndex); 38008f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long eventId = cursor.getLong(eventIdIndex); 38018f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutes = cursor.getInt(minutesIndex); 38028f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long startTime = cursor.getLong(beginIndex); 38038f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long endTime = cursor.getLong(endIndex); 38049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 38069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(alarmTime); 38079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); 38089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(startTime); 38099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 38108f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 38118f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, " looking at id: " + eventId + " " + startTime + startTimeStr 38128f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " alarm: " + alarmTime + schedTime); 38139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmTime < nextAlarmTime) { 38169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff nextAlarmTime = alarmTime; 38179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (alarmTime > 38181edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff nextAlarmTime + DateUtils.MINUTE_IN_MILLIS) { 38199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This event alarm (and all later ones) will be scheduled 38209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // later. 38218f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 38228f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "This event alarm (and all later ones) will be scheduled later"); 38238f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 38249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 38259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Avoid an SQLiteContraintException by checking if this alarm 38289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // already exists in the table. 3829e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (CalendarAlerts.alarmExists(mContentResolver, eventId, startTime, alarmTime)) { 38309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 38319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int titleIndex = cursor.getColumnIndex(Events.TITLE); 38329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String title = cursor.getString(titleIndex); 38339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, " alarm exists for id: " + eventId + " " + title); 38349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 38369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Insert this alarm into the CalendarAlerts table 3839e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio Uri uri = CalendarAlerts.insert(mContentResolver, eventId, startTime, 38409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endTime, alarmTime, minutes); 38419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (uri == null) { 3842f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 3843f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "runScheduleNextAlarm() insert into " 3844f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "CalendarAlerts table failed"); 3845f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 38469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 38479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3849e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().scheduleAlarm(alarmTime); 38509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 38529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 38539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 38549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38578f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Refresh notification bar 38588f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (rowsDeleted > 0) { 3859e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio getOrCreateCalendarAlarmManager().scheduleAlarm(currentMillis); 38608f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 38618f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 38629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we scheduled an event alarm, then schedule the next alarm check 38639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for one minute past that alarm. Otherwise, if there were no 38649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarms scheduled, then check again in 24 hours. If a new 38659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event is inserted before the next alarm check, then this method 38669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will be run again when the new event is inserted. 38679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (nextAlarmTime != Long.MAX_VALUE) { 38681edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(nextAlarmTime + DateUtils.MINUTE_IN_MILLIS); 38699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 38701edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(currentMillis + DateUtils.DAY_IN_MILLIS); 38719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 38759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Removes the entries in the CalendarAlerts table for alarms that we have 38769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled but that have not fired yet. We do this to ensure that we 38779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * don't miss an alarm. The CalendarAlerts table keeps track of the 38789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms that we have scheduled but the actual alarm list is in memory 38799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and will be cleared if the phone reboots. 38809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 38819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We don't need to remove entries that have already fired, and in fact 38829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we should not remove them because we need to display the notifications 38839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * until the user dismisses them. 38849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 38859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We could remove entries that have fired and been dismissed, but we leave 38869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * them around for a while because it makes it easier to debug problems. 38879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Entries that are old enough will be cleaned up later when we schedule 38889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * new alarms. 38899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 38909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void removeScheduledAlarmsLocked(SQLiteDatabase db) { 38919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 38929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "removing scheduled alarms"); 38939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete(CalendarAlerts.TABLE_NAME, 38959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.STATE + "=" + CalendarAlerts.SCHEDULED, null /* whereArgs */); 38969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3898a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3899a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3900a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3901a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 3902dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 3903dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * @param whether or not the update is being triggered by a sync 3904a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3905dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 3906dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 3907dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 3908a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3909a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3910a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3911a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3912a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3913a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 3914a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 3915a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 3916a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3917a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 3918a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3919a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * @param the ID of the event that changed, or -1 for no specific event 3920dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * @param whether or not the update is being triggered by a sync 3921a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3922dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 3923dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 3924a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 3925a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 3926a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 3927a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 3928a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 3929dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 3930dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 3931dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 3932dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 3933dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 3934dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 3935dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 3936dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 3937dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 3938dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 3939dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 3940dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 3941dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 3942dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 3943dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 3944dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 3945dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 3946a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 3947dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 3948a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3949a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3950a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3951a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 3952a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 3953a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #sendUpdateNotification()} instead. 3954a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3955a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * @see #sendUpdateNotification() 3956a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3957a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 3958a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 3959dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Calendar.CONTENT_URI); 3960f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 3961f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 3962f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3963e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 3964a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3965a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 39669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 39679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 39689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 39699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int DELETED_EVENTS = 4; 39709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS = 5; 39719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_ID = 6; 39729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES = 7; 39739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES_ID = 8; 39749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS = 9; 39759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS_ID = 10; 39769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES = 11; 39779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES_ID = 12; 39789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS = 13; 39799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_ID = 14; 39809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_BY_INSTANCE = 15; 39816db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int INSTANCES_BY_DAY = 16; 39826db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE = 17; 39836db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE_ID = 18; 39846db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES = 19; 39856db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES_ID = 20; 39866db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_DAYS = 21; 398783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM = 22; 398883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM_REMOVE = 23; 398948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik private static final int TIME = 24; 399043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio private static final int CALENDAR_ENTITIES = 25; 399143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio private static final int CALENDAR_ENTITIES_ID = 26; 399281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final int INSTANCES_SEARCH = 27; 399381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final int INSTANCES_SEARCH_BY_DAY = 28; 3994315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final int PROVIDER_PROPERTIES = 29; 39959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 39979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 39989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sEventsProjectionMap; 399919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 40009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 40019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 40029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 4003315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 40049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 4006b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/when/*/*", INSTANCES); 4007b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 400881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sUriMatcher.addURI(Calendar.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 400981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sUriMatcher.addURI(Calendar.AUTHORITY, "instances/searchbyday/*/*/*", 401081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 4011b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 4012b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events", EVENTS); 4013b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events/#", EVENTS_ID); 4014b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities", EVENT_ENTITIES); 4015b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 4016b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars", CALENDARS); 4017b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars/#", CALENDARS_ID); 401843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 401943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 4020b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "deleted_events", DELETED_EVENTS); 4021b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees", ATTENDEES); 4022b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees/#", ATTENDEES_ID); 4023b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders", REMINDERS); 4024b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders/#", REMINDERS_ID); 4025b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 4026b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties/#", EXTENDED_PROPERTIES_ID); 4027b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 4028b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 4029b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/by_instance", 4030b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 4031c4e53191b570e09959c5723f4d253977ba48f2d0Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate", SYNCSTATE); 403283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 403383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_PATH, SCHEDULE_ALARM); 403483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 403548587d3291c4db7f0942e1bff55b88cfa7764ba0Erik sUriMatcher.addURI(Calendar.AUTHORITY, "time/#", TIME); 4036997e2e5cb006682bc1a82441304994b458d9745dErik sUriMatcher.addURI(Calendar.AUTHORITY, "time", TIME); 4037315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "properties", PROVIDER_PROPERTIES); 40389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 40409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 40419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HTML_URI, "htmlUri"); 40429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TITLE, "title"); 40439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 40449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DESCRIPTION, "description"); 40459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.STATUS, "eventStatus"); 40469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 40479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 40489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTSTART, "dtstart"); 40499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTEND, "dtend"); 40509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 40519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DURATION, "duration"); 40529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ALL_DAY, "allDay"); 40539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.VISIBILITY, "visibility"); 40549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TRANSPARENCY, "transparency"); 40559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 40569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 40579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RRULE, "rrule"); 40589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RDATE, "rdate"); 40599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXRULE, "exrule"); 40609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXDATE, "exdate"); 40619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 40629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 40639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 40649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.LAST_DATE, "lastDate"); 40659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 40669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 40679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 40689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 40699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 40709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORGANIZER, "organizer"); 40711b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventsProjectionMap.put(Events.DELETED, "deleted"); 40729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4073e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 40741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 40751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 40761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 40779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 4078982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.COLOR, "color"); 4079982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.ACCESS_LEVEL, "access_level"); 4080982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.SELECTED, "selected"); 40811b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventsProjectionMap.put(Calendars.SYNC1, "sync1"); 40829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.TIMEZONE, "timezone"); 40839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, "ownerAccount"); 40849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4085982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 4086e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 4087e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 4088982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4089e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4090982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 40911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._ID, "_id"); 40921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ID, "_sync_id"); 40931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_VERSION, "_sync_version"); 40941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_TIME, "_sync_time"); 4095c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventsProjectionMap.put(Events._SYNC_DATA, "_sync_local_id"); 40961ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_DIRTY, "_sync_dirty"); 40971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT, "_sync_account"); 40989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT_TYPE, 40991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff "_sync_account_type"); 41009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 410146f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 410219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HTML_URI, "htmlUri"); 410319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TITLE, "title"); 410419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DESCRIPTION, "description"); 410519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 410619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.STATUS, "eventStatus"); 410719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 410819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 410919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTSTART, "dtstart"); 411019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTEND, "dtend"); 411119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DURATION, "duration"); 411219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 411319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ALL_DAY, "allDay"); 411419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.VISIBILITY, "visibility"); 411519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TRANSPARENCY, "transparency"); 411619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 411719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 411819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RRULE, "rrule"); 411919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RDATE, "rdate"); 412019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXRULE, "exrule"); 412119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXDATE, "exdate"); 412219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 412319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 412419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 412519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.LAST_DATE, "lastDate"); 412619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 412719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 412819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 412919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 413019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 413119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORGANIZER, "organizer"); 41321b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventEntitiesProjectionMap.put(Events.DELETED, "deleted"); 413319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 413419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 4135c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventEntitiesProjectionMap.put(Events._SYNC_DATA, Events._SYNC_DATA); 413619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_VERSION, Events._SYNC_VERSION); 413719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_DIRTY, Events._SYNC_DIRTY); 41381b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventEntitiesProjectionMap.put(Calendars.SYNC1, Calendars.SYNC1); 4139c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio sEventEntitiesProjectionMap.put(Events.SYNC_ADAPTER_DATA, Events.SYNC_ADAPTER_DATA); 414019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 41419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 41421b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 41439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 41449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 41459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 41469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 41479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 41489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 41499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 41509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 41519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 41539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 41549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 41559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 41569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 41579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 41589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 41599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 41609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 41629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 41639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 41649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 41659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 41669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 41689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 41699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 41709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 41719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 41729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 41739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 41749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4175315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4176315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4177315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4178315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4179315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 41809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 41839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make sure that there are no entries for accounts that no longer 41849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * exist. We are overriding this since we need to delete from the 41859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 41867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 41877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 41889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 41899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 4190ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4191ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4192ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4193ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4194ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4195ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 41969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 419746f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 419846f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 41999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 42009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 42019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 42029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 42049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 42069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 42079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4208b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio for (String table : new String[]{Tables.CALENDARS}) { 4209ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 42109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 42117cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Cursor c = mDb.rawQuery("SELECT DISTINCT " + 42127cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT + 42137cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 42147cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT_TYPE + 42157cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 42169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 42179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c.getString(0) != null && c.getString(1) != null) { 42189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 42199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 42209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 42219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 42259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 4228f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 4229f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 4230f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 42319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 4232b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 42339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 42359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 42369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 42379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 42389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42393ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 42403ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 4241dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 42429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4244636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 4245636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 4246636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 4247636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 4248636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 4249636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 4250636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 4251636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 4252636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 4253636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 4254636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 4255636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 4256636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 4257636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 4258636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 4259636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 4260636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 4261636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 4262636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 42639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 4264