CalendarProvider2.java revision b7c010fdc02695b692cd74acf432e8ccb3bda70c
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; 469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 47f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglioimport android.pim.EventRecurrence; 489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.pim.RecurrenceSet; 499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar; 519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Attendees; 529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.CalendarAlerts; 539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Calendars; 549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Events; 559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Instances; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Reminders; 579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 581edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 59192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 62ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 65ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 68dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.Set; 709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 71dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * is defined in {@link android.provider.Calendar}. 779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String TAG = "CalendarProvider2"; 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 827be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 837be45683e367bd6897daf6444b03be938f8f5eaaErik 849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean PROFILE = false; 859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 868f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 878f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff private static final String INVALID_CALENDARALERTS_SELECTOR = 88d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang "_id IN (SELECT ca." + CalendarAlerts._ID + " FROM " 89d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + Tables.CALENDAR_ALERTS + " AS ca" 90d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Tables.INSTANCES 91d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " USING (" + Instances.EVENT_ID + "," 92d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + Instances.BEGIN + "," + Instances.END + ")" 93d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Tables.REMINDERS + " AS r ON" 94d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " (ca." + CalendarAlerts.EVENT_ID + "=r." + Reminders.EVENT_ID 95d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND ca." + CalendarAlerts.MINUTES + "=r." + Reminders.MINUTES + ")" 96d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " LEFT OUTER JOIN " + Views.EVENTS + " AS e ON" 97d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " (ca." + CalendarAlerts.EVENT_ID + "=e." + Events._ID + ")" 98d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " WHERE " + Tables.INSTANCES + "." + Instances.BEGIN + " ISNULL" 99d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR ca." + CalendarAlerts.ALARM_TIME + "<?" 100d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR (r." + Reminders.MINUTES + " ISNULL" 101d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND ca." + CalendarAlerts.MINUTES + "<>0)" 102d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " OR e." + Calendars.SELECTED + "=0)"; 1038f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 1041ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 1051ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 1069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 1089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 1099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 1119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 1129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 1167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_ORIGINAL_EVENT_INDEX = 3; 1177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 126646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 127646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 128646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 129646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 130646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 131646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 132646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 133646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 134646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 135646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 136646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 137646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 138646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 139646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 140646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 141646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 147ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 1509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 151315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final Uri SYNCSTATE_CONTENT_URI = Uri.parse("content://syncstate/state"); 15283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // 15383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_URI runs scheduleNextAlarm(false) 15483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_REMOVE_URI runs scheduleNextAlarm(true) 15583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: use a service to schedule alarms rather than private URI 15683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_PATH = "schedule_alarms"; 15783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; 15883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_URI = 15983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_PATH); 16083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_REMOVE_URI = 16183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); 1629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // To determine if a recurrence exception originally overlapped the 1649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window, we need to assume a maximum duration, since we only know 1659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original start time. 1669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int MAX_ASSUMED_DURATION = 7*24*60*60*1000; 1679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1688ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 1698ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Due to an issue in Calendar Server restricting the length of the name we had to strip it down 1708ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1718ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1728ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1738ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1748ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1753443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 176c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.EVENT_ID + ", " + 177c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.DTSTART_2445 + ", " + 178c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.DTEND_2445 + ", " + 1793443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1803443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 1813443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio "EventsRawTimes" + ", " + 1823443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio "Events" + 1833443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 184c2d2953fa4ac4bf9066f40d97858e69e519269f1Fabrice Di Meglio Calendar.EventsRawTimesColumns.EVENT_ID + " = " + "Events." + Events._ID; 1853443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio 1869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class TimeRange { 1879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean allDay; 1909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesRange { 1939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public InstancesRange(long begin, long end) { 1979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.begin = begin; 1989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.end = end; 1999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesList 2039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends ArrayList<ContentValues> { 2049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class EventInstancesMap 2079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends HashMap<String, InstancesList> { 2081030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff public void add(String syncIdKey, ContentValues values) { 2091030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList instances = get(syncIdKey); 2109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (instances == null) { 2119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances = new InstancesList(); 2121030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff put(syncIdKey, instances); 2139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances.add(values); 2159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A thread that runs in the background and schedules the next 2199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // calendar event alarm. 2209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class AlarmScheduler extends Thread { 2219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean mRemoveAlarms; 2229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public AlarmScheduler(boolean removeAlarms) { 2249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mRemoveAlarms = removeAlarms; 2259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 227192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank @Override 2289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 2299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 2309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 2319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff runScheduleNextAlarm(mRemoveAlarms); 2329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e) { 233f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 234f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "runScheduleNextAlarm() failed", e); 235f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We search backward in time for event reminders that we may have missed 2429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and schedule them if the event has not yet expired. The amount in 2439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the past to search backwards is controlled by this constant. It 2449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should be at least a few minutes to allow for an event that was 2459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * recently created on the web to make its way to the phone. Two hours 2469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * might seem like overkill, but it is useful in the case where the user 2479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * just crossed into a new timezone and might have just missed an alarm. 2489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2491edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff private static final long SCHEDULE_ALARM_SLACK = 2 * DateUtils.HOUR_IN_MILLIS; 2509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Alarms older than this threshold will be deleted from the CalendarAlerts 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. This should be at least a day because if the timezone is 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * wrong and the user corrects it we might delete good alarms that 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * appear to be old because the device time was incorrectly in the future. 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This threshold must also be larger than SCHEDULE_ALARM_SLACK. We add 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the SCHEDULE_ALARM_SLACK to ensure this. 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 2599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * To make it easier to find and debug problems with missed reminders, 2609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * set this to something greater than a day. 2619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long CLEAR_OLD_ALARM_THRESHOLD = 2631edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff 7 * DateUtils.DAY_IN_MILLIS + SCHEDULE_ALARM_SLACK; 2649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A lock for synchronizing access to fields that are shared 2669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // with the AlarmScheduler thread. 2679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Object mAlarmLock = new Object(); 2689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allocate the string constant once here instead of on the heap 2789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String CALENDAR_ID_SELECTION = "calendar_id=?"; 2799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 28181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 28281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 28381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 28481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 28581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + Calendar.Instances.EVENT_ID + "=" + 28681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 28781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + Calendar.Events._ID + ")"; 28881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 28918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 29018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 29118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 29218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 29318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 29418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Instances.EVENT_ID + "=" + 29518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 29618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Events._ID + ")" + ") LEFT OUTER JOIN " + 29718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 29818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 29918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Attendees.EVENT_ID + "=" + 30018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 30118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang + Calendar.Events._ID + ")"; 30218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 30381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String BETWEEN_DAY_WHERE = 30481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.START_DAY + "<=? AND " + 30581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.END_DAY + ">=?"; 30681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 30781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String BETWEEN_WHERE = 30881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.BEGIN + "<=? AND " + 30981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Instances.END + ">=?"; 3109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 3129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 3139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 3149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 3159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 3169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 31881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * A regex for describing how we split search queries into tokens. 319dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Keeps quoted phrases as one token. 320dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 321dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * "one \"two three\"" ==> ["one" "two three"] 322dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 323dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 324dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 325dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 326dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 327dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 328dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 329dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 330dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 331dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 33281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 333dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 334dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 335dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 336dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 337dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 338dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 339dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 340dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 341dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 34281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 34318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 34418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 34518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 34618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 34718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 34818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang "group_concat(" + Calendar.Attendees.ATTENDEE_EMAIL + ")"; 34918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 35018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 35118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 35218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 35318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 35418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 35518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang "group_concat(" + Calendar.Attendees.ATTENDEE_NAME + ")"; 35618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 35781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 35881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Events.TITLE, 35981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang Calendar.Events.DESCRIPTION, 36018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang Calendar.Events.EVENT_LOCATION, 36118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 36218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 36381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 36481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 3659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager mAlarmManager; 3669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 367a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 368a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 369a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 370a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 371a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 372a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 373a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 374a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 375a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 376a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 377a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 378a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 379dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 380dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 381dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 382dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 383dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 384dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 385a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 386a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 387a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 388a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 389dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 390a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 391a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 392a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 393dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 394dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 395dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 396dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 397dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 398a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 399a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 400a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 401a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 4159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 4199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 4219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 4229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void verifyAccounts() { 4279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 4289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff onAccountsUpdated(AccountManager.get(getContext()).getAccounts()); 4299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 4399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 440ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 441ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 442ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 443f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 444f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 445f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 446ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 447ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 448ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 450ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 451dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 452ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 453ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 4569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 4579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 4599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 4609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 4619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Context c = getContext(); 4629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 4649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 4659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 4669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.registerReceiver(mIntentReceiver, filter); 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData = new MetaData(mDbHelper); 470ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 471ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 472ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio postInitialize(); 4739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 477ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 478ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 479ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 480ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 481ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 482ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 483ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 484ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 485ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 486ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 487ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 488ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 489ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 490ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 491ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 492ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 4939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 4959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 496315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 4979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 4999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 5019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 5049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 5069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 507ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 5089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 512315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 513315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 514315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 515315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 516315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 517315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 518315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 519315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 5209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 5219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 5229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 523315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 524ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 525315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 526315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 527a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 528315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 529315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 530315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 531ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 532315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 533315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 534ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 535315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 536ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 537ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 538ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 539ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 540ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio rescheduleMissedAlarms(); 541ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 542ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 543f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 544f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 545f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 546ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 547ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 548ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 549ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 550ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 551f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 552f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 553f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 554ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 556ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 557ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 558315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 559ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 560ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 5613443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 562ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 563315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 564ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 565ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 566ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 567ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 568ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 569ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 570ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 5713443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 5723443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 573ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 574ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 575ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 576ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 577ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 5783443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 579f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 580f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 581f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 582f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 583f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 584f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 585f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 586ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 5873443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 588ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 589ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 590ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 591ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 592ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 593ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 594ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 595ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 596ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 597ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 598ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 599f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 600f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 601f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 602ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 603ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 604ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 605ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 606ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 607ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 608f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 609f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 610f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 611ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 612ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 613ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 614ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 615ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 616ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 617ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 618ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 619ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 620ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtstart", get2445ToMillis(timezone, dtStart2445)); 621ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtend", get2445ToMillis(timezone, dtEnd2445)); 622ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 623dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff int result = mDb.update("Events", values, "_id=?", 624dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 625ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 626ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 627ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 628ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 629ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 630ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 631ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 632ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 633ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 634ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 635ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 636f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 637f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 638f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 639ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 640ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 6419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 642ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 643ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 644ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 645ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 646315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 647315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 648ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 649ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 650ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 651ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 652ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 65325e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 654ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 655315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 656315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 657ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 659f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 660f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 661f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 662ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 663ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 664ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 665315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 666315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 667315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 668315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 669315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 6719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 6729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 6739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 6749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 675315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 676315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 6779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 6789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 6799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 6809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 6819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 6821f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 6839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 6849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 6851f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 6861f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 6871f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 6881f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 6891f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 6901f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 691d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio null /* selection */, null /* sort */, 692d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 693315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 694315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 695315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone()); 6961f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 6971f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 6981f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 6991f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7001f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 7039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void rescheduleMissedAlarms() { 7069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 7079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager != null) { 7089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 7099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = context.getContentResolver(); 7109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.rescheduleMissedAlarms(cr, context, manager); 7119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 7159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Appends comma separated ids. 7169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param ids Should not be empty 7179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 7189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void appendIds(StringBuilder sb, HashSet<Long> ids) { 7199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long id : ids) { 7209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.append(id).append(','); 7219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.setLength(sb.length() - 1); // Yank the last comma 7249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 727b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 7289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 7299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 7309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff getContext().getContentResolver().notifyChange(Calendar.CONTENT_URI, null, 731b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio syncToNetwork); 7329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 7359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 7369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 737ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 738ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 7399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 7429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 7449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 7459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 746315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 7479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 7499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 7509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 7519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 7529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 7539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 7551ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 757595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 7589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 7601ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 762636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 763636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 7649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 76519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 76619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 76719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 76819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 769595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 77019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 77119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 77219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 77319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 774636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 775636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 77619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 77719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 7789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 77943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 7809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 781595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 7829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 78443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 7859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 786636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 787636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 7889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 7909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 7919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 7929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 7939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 7949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 805315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return handleInstanceQuery(qb, begin, end, projection, 807d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio selection, sortOrder, match == INSTANCES_BY_DAY, 808315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 809315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 81081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 81181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 81281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 81381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 81481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 81581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 81681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 81781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 81881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 81981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 82081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 82181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 82281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 82381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 824315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 82581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 82681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 82781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return handleInstanceSearchQuery(qb, begin, end, query, projection, 828315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio selection, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 829315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8306db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 8319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 8329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 8339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 8359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 8379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 8419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 8439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 845315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 846315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 847315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 8491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 8509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 8511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.appendWhere("Events._id=Attendees.event_id"); 8529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 8541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 8559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 856636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 857636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Attendees._id=? AND Events._id=Attendees.event_id"); 8589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 8609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Reminders"); 8619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 8631ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Reminders, Events"); 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 865636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 866636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Reminders._id=? AND Events._id=Reminders.event_id"); 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 869e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 871e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 872e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 875e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 877e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 878e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 882e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 8839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 884636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 885e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 886e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id AND CalendarAlerts._id=?"); 8879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 8899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("ExtendedProperties"); 8909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 8927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff qb.setTables("ExtendedProperties"); 893636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 894636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("ExtendedProperties._id=?"); 8959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 896315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 897315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setTables("CalendarCache"); 898315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 899315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 9069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 9099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 9109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 911ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 912ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 913ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 914ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 915ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 916ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 917ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 918ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 919ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 9219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 9239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.setNotificationUri(getContext().getContentResolver(), Calendar.Events.CONTENT_URI); 9259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 9279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 9329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 9349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 940d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 941315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 942315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 9439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 9449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 946d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio long rangeEnd, String[] projection, String selection, String sort, 947315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean searchByDay, boolean forceExpansion, String instancesTimezone, 948315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 9499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 95081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 9519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 9539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 9549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 955315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 9589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 9609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 961315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 962315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 96381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.appendWhere(BETWEEN_DAY_WHERE); 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 966315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 967315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 96881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.appendWhere(BETWEEN_WHERE); 9699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9708335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(rangeEnd), 9718335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 9728335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 9737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 97681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 977dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 978dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 979dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 980dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 981dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 982dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 983dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 984dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 985dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 986dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 987dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 98881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 989dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 990dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 99181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 99281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 99381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 99481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 99581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 99681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 997dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 998dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 999dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1000dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1001dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1002dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1003dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1004dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1005dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1006dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1007dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1008dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1009dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1010dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 101181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 101281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 101381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 101481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 101581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 101681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 101781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 101881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 101981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 102081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 102181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 102281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 102381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 102481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 102581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 102681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 102781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 102881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 102981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 103081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * approximation of full-text search across multiple columns. 103181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 103281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 103381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 103481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 103581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 103681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 103781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 103881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 103981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 104081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 104181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 104281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1043dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1044dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1045dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 104681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 104781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 104881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 104981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 105018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 105118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 105218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 105318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 105481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 105581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 105681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 105781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 105881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 105981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 106018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 106118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 106281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 106318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 106418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 106518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 106681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1067f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1068f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 106918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 107081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 107181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 107281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 107381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 107481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 107581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 107681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 1077315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String selection, String sort, boolean searchByDay, String instancesTimezone, 1078315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 107918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 108081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 108181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1082dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 108381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] selectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 108418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 108518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1086dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1087dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 108881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 108981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 109081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1091315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 109281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 109381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 109481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 109581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 109681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 109718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 109818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 109956292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 110056292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1101315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1102315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1103315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 110456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 110581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.appendWhere(BETWEEN_DAY_WHERE); 110681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 110781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 110818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 110918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 111056292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 111156292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1112315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1113315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1114315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 111556292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 111681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.appendWhere(BETWEEN_WHERE); 111781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 111918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 112018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang Instances._ID /* groupBy */, searchWhere /* having */, sort); 112181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 112281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 11236db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1124315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1125315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 112681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 11276db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 112843556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 112943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1130315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1131192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 113243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 113343556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1134192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 113543556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1136315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1137315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 113881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.appendWhere(BETWEEN_DAY_WHERE); 11398335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 11408335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 11418335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 11426db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 11439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 11469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 11479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. Acquires the database lock and calls {@link #acquireInstanceRangeLocked}. 11489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 11499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 11509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 11519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1152d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1153315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1154315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 11559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1156d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1157315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1158315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 11599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 11609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1161315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1162315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 11639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 11649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 11659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 11669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 11709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 11719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 11729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 11739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 11749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 11759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1176315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1177315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1178315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 11799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1180315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1181315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 11829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 11839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 11849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1185315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1186315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1187315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1188315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1189315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 11909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 11919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 11929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 11939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 11949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 11959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 11969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 11979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 11989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 11999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 12039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 12049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 12059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 12069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 12079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1208315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1209315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1210315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1211315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1212315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1213315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1214315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 12157be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 12167be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 12177be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 12187be45683e367bd6897daf6444b03be938f8f5eaaErik } 1219315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1220315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1221315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1222d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 12239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 12249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.execSQL("DELETE FROM Instances;"); 1225f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 12266db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 12279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 12289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1229315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1230315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1231315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 12329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1233315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 12347be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 12357be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 12367be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 12377be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 12387be45683e367bd6897daf6444b03be938f8f5eaaErik 12397be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1240315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 12417be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 12427be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 12437be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 12447be45683e367bd6897daf6444b03be938f8f5eaaErik } 1245315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 12469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 12479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 12509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 12519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 12529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 12549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 12559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 12569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 12579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 12589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 12599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 12609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1261f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 12629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 12639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 12649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 12669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1271315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1278315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 12799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 12809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 12849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EXPAND_COLUMNS = new String[] { 12879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._ID, 12889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 12899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.STATUS, 12909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTSTART, 12919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTEND, 12929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EVENT_TIMEZONE, 12939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 12949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 12959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXRULE, 12969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXDATE, 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DURATION, 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ALL_DAY, 12999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 13001030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff Events.ORIGINAL_INSTANCE_TIME, 13011dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio Events.CALENDAR_ID, 13021b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio Events.DELETED 13039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 13049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make instances for the given range. 13079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 13089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void expandInstanceRangeLocked(long begin, long end, String localTimezone) { 13099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 13119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.startMethodTracing("expandInstanceRangeLocked"); 13129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 13159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Expanding events between " + begin + " and " + end); 13169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getEntries(begin, end); 13199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff performInstanceExpansion(begin, end, localTimezone, entries); 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.stopMethodTracing(); 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Get all entries affecting the given window. 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return Cursor for the entries; caller must close it. 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getEntries(long begin, long end) { 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 13391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 13409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String beginString = String.valueOf(begin); 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String endString = String.valueOf(end); 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // grab recurrence exceptions that fall outside our expansion window but modify 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences that do fall within our window. we won't insert these into the output 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set of instances, but instead will just add them to our cancellations list, so we 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // can cancel the correct recurrence expansion instances. 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we don't have originalInstanceDuration or end time. for now, assume the original 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance lasts no longer than 1 week. 13512d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio // also filter with syncable state (we dont want the entries from a non syncable account) 13529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: compute the originalInstanceEndTime or get this from the server. 13532d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio qb.appendWhere("((dtstart <= ? AND (lastDate IS NULL OR lastDate >= ?)) OR " + 13548335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff "(originalInstanceTime IS NOT NULL AND originalInstanceTime <= ? AND " + 13552d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio "originalInstanceTime >= ?)) AND (sync_events != 0)"); 13568335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {endString, beginString, endString, 13578335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(begin - MAX_ASSUMED_DURATION)}; 1358e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Cursor c = qb.query(mDb, EXPAND_COLUMNS, null /* selection */, 13598335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff selectionArgs, null /* groupBy */, 13607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 1361e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 1362e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.v(TAG, "Instance expansion: got " + c.getCount() + " entries"); 1363e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1364e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff return c; 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13681030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * Generates a unique key from the syncId and calendarId. 13691030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * The purpose of this is to prevent collisions if two different calendars use the 13701030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * same sync id. This can happen if a Google calendar is accessed by two different accounts, 13711030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * or with Exchange, where ids are not unique between calendars. 13721030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param syncId Id for the event 13731030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param calendarId Id for the calendar 13741030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @return key 13751030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff */ 13761030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff private String getSyncIdKey(String syncId, long calendarId) { 13771030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff return calendarId + ":" + syncId; 13781030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff } 13791030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 13801030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff /** 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Perform instance expansion on the given entries. 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param localTimezone 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param entries The entries to process. 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 13879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void performInstanceExpansion(long begin, long end, String localTimezone, 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries) { 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13911030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // Key into the instance values to hold the original event concatenated with calendar id. 13921030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff final String ORIGINAL_EVENT_AND_CALENDAR = "ORIGINAL_EVENT_AND_CALENDAR"; 13931030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int statusColumn = entries.getColumnIndex(Events.STATUS); 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtstartColumn = entries.getColumnIndex(Events.DTSTART); 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtendColumn = entries.getColumnIndex(Events.DTEND); 13979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int eventTimezoneColumn = entries.getColumnIndex(Events.EVENT_TIMEZONE); 13989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int durationColumn = entries.getColumnIndex(Events.DURATION); 13999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rruleColumn = entries.getColumnIndex(Events.RRULE); 14009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rdateColumn = entries.getColumnIndex(Events.RDATE); 14019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exruleColumn = entries.getColumnIndex(Events.EXRULE); 14029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exdateColumn = entries.getColumnIndex(Events.EXDATE); 14039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int allDayColumn = entries.getColumnIndex(Events.ALL_DAY); 14049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int idColumn = entries.getColumnIndex(Events._ID); 14059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int syncIdColumn = entries.getColumnIndex(Events._SYNC_ID); 14069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalEventColumn = entries.getColumnIndex(Events.ORIGINAL_EVENT); 14079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalInstanceTimeColumn = entries.getColumnIndex(Events.ORIGINAL_INSTANCE_TIME); 14081030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff int calendarIdColumn = entries.getColumnIndex(Events.CALENDAR_ID); 14091b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio int deletedColumn = entries.getColumnIndex(Events.DELETED); 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues initialValues; 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff EventInstancesMap instancesMap = new EventInstancesMap(); 14139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time eventTime = new Time(); 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: entries contains all events that affect the current 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window. It consists of: 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. These will be 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed. 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Recurrences that included the window. These will be displayed 14229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if not canceled. 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Recurrence exceptions that fall in the window. These will be 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed if not cancellations. 14259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 14269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window (subject to 1 week assumption above), but are outside 14279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the window. These will not be displayed. Cases c and d are 14289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // distingushed by the start / end time. 14299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (entries.moveToNext()) { 14319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 14329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = null; 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = entries.getInt(allDayColumn) != 0; 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String eventTimezone = entries.getString(eventTimezoneColumn); 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(eventTimezone)) { 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the events table, allDay events start at midnight. 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this forces them to stay at midnight for all day events 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: check that this actually does the right thing. 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTimezone = Time.TIMEZONE_UTC; 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = entries.getLong(dtstartColumn); 14459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long eventId = Long.valueOf(entries.getLong(idColumn)); 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = entries.getString(durationColumn); 14489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 14499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 14509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 14519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff catch (DateException e) { 1453f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1454f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "error parsing duration for event " 1455f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + eventId + "'" + durationStr + "'", e); 1456f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 14609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 14619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 14629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 14639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 14649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String syncId = entries.getString(syncIdColumn); 14689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = entries.getString(originalEventColumn); 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalInstanceTimeMillis = -1; 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(originalInstanceTimeColumn)) { 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis= entries.getLong(originalInstanceTimeColumn); 14739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = entries.getInt(statusColumn); 14751dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio boolean deleted = (entries.getInt(deletedColumn) != 0); 14769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rruleStr = entries.getString(rruleColumn); 14789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rdateStr = entries.getString(rdateColumn); 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exruleStr = entries.getString(exruleColumn); 14809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exdateStr = entries.getString(exdateColumn); 14811030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff long calendarId = entries.getLong(calendarIdColumn); 14821030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String syncIdKey = getSyncIdKey(syncId, calendarId); // key into instancesMap 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1484f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 1485f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 1486f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(rruleStr, rdateStr, exruleStr, exdateStr); 1487f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 1488f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1489f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + rruleStr, e); 1490f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 1491f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio continue; 1492f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1494f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 14959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating 14969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (status == Events.STATUS_CANCELED) { 14989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen! 1499f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1500f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Found canceled recurring event in " 1501f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "Events table. Ignoring."); 1502f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 15039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 15049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1505370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik if (deleted) { 1506370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik // Don't expand deleted recurring events 1507370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik continue; 1508370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik } 15099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // need to parse the event into a local calendar. 15119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = eventTimezone; 15129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.set(dtstartMillis); 15139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.allDay = allDay; 15149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 15169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen. 1517f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1518f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Repeating event has no duration -- " 1519f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "should not happen."); 1520f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 15219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 15229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set to one day. 15239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 15249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 15259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 1; 15269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 15279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 15289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 15299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P1D"; 15309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 15319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // compute the duration from dtend, if we can. 15329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // otherwise, use 0s. 15339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 15349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 15359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 15369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 15379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 15389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 15399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = entries.getLong(dtendColumn); 15409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = (int) ((dtendMillis - dtstartMillis) / 1000); 15419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P" + duration.seconds + "S"; 15429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 15439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 15449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 15459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long[] dates; 15509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dates = rp.expand(eventTime, recur, begin, end); 15519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Initialize the "eventTime" timezone outside the loop. 15539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is used in computeTimezoneDependentFields(). 15549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 15559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 15569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 15579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 15589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long durationMillis = duration.getMillis(); 15619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long date : dates) { 15629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 15639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 15649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.BEGIN, date); 15669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = date + durationMillis; 15679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 15689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(date, dtendMillis, 15709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 15711030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 15729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 15749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating 15759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 15769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if this event has an "original" field, then record 15789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that we need to cancel the original event (we can't 15799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do that here because the order of this loop isn't 15809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // defined) 15819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 15821030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // The ORIGINAL_EVENT_AND_CALENDAR holds the 15831030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // calendar id concatenated with the ORIGINAL_EVENT to form 15841030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // a unique key, matching the keys for instancesMap. 15851030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff initialValues.put(ORIGINAL_EVENT_AND_CALENDAR, 15861030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff getSyncIdKey(originalEvent, calendarId)); 15879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.ORIGINAL_INSTANCE_TIME, 15889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis); 15899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, status); 15909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = dtstartMillis; 15939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 15949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 15959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = entries.getLong(dtendColumn); 15969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 15989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = duration.addTo(dtstartMillis); 15999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this non-recurring event might be a recurrence exception that doesn't 16029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // actually fall within our expansion window, but instead was selected 16039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so we can correctly cancel expanded recurrence instances below. do not 16049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // add events to the instances map if they don't actually fall within our 16059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansion window. 16069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((dtendMillis < begin) || (dtstartMillis > end)) { 16079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 16089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, Events.STATUS_CANCELED); 16099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 1610f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1611f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Unexpected event outside window: " + syncId); 1612f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 16139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 16149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 16189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16191dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio initialValues.put(Instances.BEGIN, dtstartMillis); 16209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 16219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16221dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // we temporarily store the DELETED status (will be cleaned later) 16231b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio initialValues.put(Events.DELETED, deleted); 16241dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 16259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 16269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 16279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 16289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 16299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, 16319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 16329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16331030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 16349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 1636f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1637f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 1638f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 16399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (TimeFormatException e) { 1640f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 1641f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 1642f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 16439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains all instances that affect the 16471030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // window, indexed by original sync id concatenated with calendar id. 16481030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // It consists of: 16499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. They have: 16509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 16519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window. They may 16529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be subject to exceptions. They have: 16539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 16549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They have: 16551030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS (since they can 16569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be a modification or cancellation), EVENT_ID, BEGIN, END 16579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 16589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. They have: 16591030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS = 16609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED, EVENT_ID, BEGIN, END 16619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // First, delete the original instances corresponding to recurrence 16639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exceptions. We do this by iterating over the list and for each 16649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrence exception, we search the list for an instance with a 16659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // matching "original instance time". If we find such an instance, 16669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we remove it from the list. If we don't find such an instance 16679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then we cancel the recurrence exception. 16689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Set<String> keys = instancesMap.keySet(); 16691030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 16701030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 16719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 16729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If this instance is not a recurrence exception, then 16749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // skip it. 16751030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff if (!values.containsKey(ORIGINAL_EVENT_AND_CALENDAR)) { 16769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 16779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16791030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String originalEventPlusCalendar = values.getAsString(ORIGINAL_EVENT_AND_CALENDAR); 16809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 16811030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList originalList = instancesMap.get(originalEventPlusCalendar); 16829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalList == null) { 16839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The original recurrence is not present, so don't try canceling it. 16849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 16859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Search the original event for a matching original 16889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance time. If there is a matching one, then remove 16899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original one. We do this both for exceptions that 16909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // change the original instance as well as for exceptions 16919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that delete the original instance. 16929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (int num = originalList.size() - 1; num >= 0; num--) { 16939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues originalValues = originalList.get(num); 16949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginTime = originalValues.getAsLong(Instances.BEGIN); 16959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (beginTime == originalTime) { 16969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We found the original instance, so remove it. 16979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalList.remove(num); 16989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains filtered instances. 17049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // It consists of: 17059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. 17069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window and have not 17079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // been subject to exceptions. 17089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They will have 17099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED if they are cancellations. 17109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 17119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. These are STATUS_CANCELED. 17129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do the inserts. Since the db lock is held when this method is executed, 17149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this will be done in a transaction. 17159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if there is lock contention (e.g., a sync is trying to merge into the db 17169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // while the calendar app is trying to query the db (expanding instances)), we will 17179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // not be "polite" and yield the lock until we're done. This will favor local query 17189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // operations over sync/write operations. 17191030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 17201030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 17219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 17229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17231dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // If this instance was cancelled or deleted then don't create a new 17249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance. 17259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer status = values.getAsInteger(Events.STATUS); 17261b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio boolean deleted = values.containsKey(Events.DELETED) ? 17271b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.getAsBoolean(Events.DELETED) : false; 17281dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio if ((status != null && status == Events.STATUS_CANCELED) || deleted) { 17299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 17309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17321dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // We remove this useless key (not valid in the context of Instances table) 17331b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.remove(Events.DELETED); 17341dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 17359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Remove these fields before inserting a new instance 17361030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff values.remove(ORIGINAL_EVENT_AND_CALENDAR); 17379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 17389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.STATUS); 17399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1740c874ed5c6cc0fcc6ac06ae7d20db0eab7d749608Ken Shirriff mDbHelper.instancesReplace(values); 17419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 17469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Computes the timezone-dependent fields of an instance of an event and 17479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * updates the "values" map to contain those fields. 17489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 17499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin the start time of the instance (in UTC milliseconds) 17509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end the end time of the instance (in UTC milliseconds) 17519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param local a Time object with the timezone set to the local timezone 17529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values a map that will contain the timezone-dependent fields 17539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 17549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void computeTimezoneDependentFields(long begin, long end, 17559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local, ContentValues values) { 17569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(begin); 17579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay = Time.getJulianDay(begin, local.gmtoff); 17589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startMinute = local.hour * 60 + local.minute; 17599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(end); 17619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay = Time.getJulianDay(end, local.gmtoff); 17629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endMinute = local.hour * 60 + local.minute; 17639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Special case for midnight, which has endMinute == 0. Change 17659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that to +24 hours on the previous day to make everything simpler. 17669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Exception: if start and end minute are both 0 on the same day, 17679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then leave endMinute alone. 17689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (endMinute == 0 && endDay > startDay) { 17699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endMinute = 24 * 60; 17709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay -= 1; 17719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_DAY, startDay); 17749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_DAY, endDay); 17759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_MINUTE, startMinute); 17769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_MINUTE, endMinute); 17779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 17809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 17819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 17829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 17839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 17849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 17859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 17869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 17879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 17889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 17899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 17909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 17919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 17929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 17939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 17949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 17959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 17969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 17979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 17989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 17996db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 18009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 180148587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 180248587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1803315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1804315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 18059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 18069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 18079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static boolean isRecurrenceEvent(ContentValues values) { 18119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return (!TextUtils.isEmpty(values.getAsString(Events.RRULE))|| 18129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.RDATE))|| 18139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT))); 18149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1816646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1817646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1818646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1819646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1820646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * corrects the fields DTSTART, DTEND, and DURATION if necessary. Also checks to ensure that 1821646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * either both DTSTART and DTEND or DTSTART and DURATION are set for each event. 1822646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1823646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @param updatedValues The values to check and correct 1824646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1825646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1826646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private boolean fixAllDayTime(Uri uri, ContentValues updatedValues) { 1827646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1828646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (updatedValues.containsKey(Events.ALL_DAY) 1829646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik && updatedValues.getAsInteger(Events.ALL_DAY).intValue() == 1) { 1830646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtstart = updatedValues.getAsLong(Events.DTSTART); 1831646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtend = updatedValues.getAsLong(Events.DTEND); 1832646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String duration = updatedValues.getAsString(Events.DURATION); 1833646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Time time = new Time(); 1834646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Cursor currentTimesCursor = null; 1835646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String tempValue; 1836646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If a complete set of time fields doesn't exist query the db for them. A complete set 1837646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // is dtstart and dtend for non-recurring events or dtstart and duration for recurring 1838646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // events. 1839646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if(dtstart == null || (dtend == null && duration == null)) { 1840646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we have an id to search for, if not this is probably a new event 1841646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 2) { 1842646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = query(uri, 1843646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik ALLDAY_TIME_PROJECTION, 1844646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selection */, 1845646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selectionArgs */, 1846646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* sort */); 1847646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1848646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (!currentTimesCursor.moveToFirst() || 1849646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.getCount() != 1) { 1850646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Either this is a new event or the query is too general to get data 1851646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // from the db. In either case don't try to use the query and catch 1852646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // errors when trying to update the time fields. 1853646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1854646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = null; 1855646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1856646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1857646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1858646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1859646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1860646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Ensure dtstart exists for this event (always required) and set so h,m,s are 0 if 1861646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // necessary. 1862646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // TODO Move this somewhere to check all events, not just allDay events. 1863646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtstart == null) { 1864646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1865646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields, we'd like to know if a field is empty 1866646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1867646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTSTART_INDEX); 1868646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1869646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtstart = Long.valueOf(tempValue); 1870646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1871646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1872646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("Event has no DTSTART field, the db " + 1873646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "may be damaged. Set DTSTART for this event to fix."); 1874646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1875646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 1876646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTSTART cannot be empty for new events."); 1877646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1878646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1879646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1880646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtstart.longValue()); 1881646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1882646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1883646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1884646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1885646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTSTART, time.toMillis(true)); 1886646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1887646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1888646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1889646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If dtend exists for this event make sure it's h,m,s are 0. 1890646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend == null && currentTimesCursor != null) { 1891646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields. We'd like to know if a field is empty 1892646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1893646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTEND_INDEX); 1894646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1895646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = Long.valueOf(tempValue); 1896646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1897646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = null; 1898646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1899646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1900646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend != null) { 1901646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1902646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtend.longValue()); 1903646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1904646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1905646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1906646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1907646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = time.toMillis(true); 1908646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTEND, dtend); 1909646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1910646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1911646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1912646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1913646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1914646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null) { 1915646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = currentTimesCursor.getString(ALLDAY_DURATION_INDEX); 1916646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1917646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1918646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1919646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1920646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration != null) { 1921646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int len = duration.length(); 1922646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1923646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * in the seconds format, and if so converts it to days. 1924646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1925646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (len == 0) { 1926646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = null; 1927646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else if (duration.charAt(0) == 'P' && 1928646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration.charAt(len - 1) == 'S') { 1929646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1930646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1931646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = "P" + days + "D"; 1932646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DURATION, duration); 1933646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1934646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1935646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1936646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1937646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null && dtend == null) { 1938646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 1939646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "an event."); 1940646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1941646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1942646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1943646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1944646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 19459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 1946b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 1947ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 19489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 19499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 19529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1953b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final int match = sUriMatcher.match(uri); 19549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 19559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 19569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 19579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 19589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 19597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 19607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 19617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 19629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 19639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 19649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 1966e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 1967e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff validateEventData(updatedValues); 1968e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 1969e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 19709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 19719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 19729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 19739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 19759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 19769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 19779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 19789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 19799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 19809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 19819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 19829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 19839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 19849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1986646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, updatedValues)) { 1987f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 1988f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 1989f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 1990f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 1991646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 19929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 19939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 19949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 19959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, true /* new event */, mDb); 19969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 19989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 19999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 20009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 20019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 20029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 20039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 20059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20068ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // if the Event Timezone is defined, store it as the original one in the 20078ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // ExtendedProperties table 20088ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) { 20098ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE); 20108ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20118ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues expropsValues = new ContentValues(); 20128ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.EVENT_ID, id); 20138ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.NAME, 20148ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio EXT_PROP_ORIGINAL_TIMEZONE); 20158ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.VALUE, originalTimezone); 20168ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20178ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Insert the extended property 20188ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues); 20198ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (exPropId == -1) { 20208ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20218ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot add the original Timezone in the " 20228ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + "ExtendedProperties table for Event: " + id); 20238ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20248ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } else { 20258ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Update the Event for saying it has some extended properties 20268ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues eventValues = new ContentValues(); 20278ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1"); 20288ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio int result = mDb.update("Events", eventValues, "_id=?", 20298ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio new String[] {String.valueOf(id)}); 20308ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (result <= 0) { 20318ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20328ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot update hasExtendedProperties column" 20338ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + " for Event: " + id); 20348ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20358ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20368ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20378ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 2038dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 20399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 20429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 20439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 20449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountName = values.getAsString(Calendars._SYNC_ACCOUNT); 20459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars._SYNC_ACCOUNT_TYPE); 20479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 20481b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio String eventsUrl = values.getAsString(Calendars.SYNC1); 20491b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 20509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2052dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 20539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 20559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 20579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.attendeesInsert(values); 20607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Attendees.EVENT_ID)); 20627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 20659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 20669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.remindersInsert(values); 20737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Reminders.EVENT_ID)); 20757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 20789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 20829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 20849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 20859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 20869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 20892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 20902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 20919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 20939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Calendar.ExtendedProperties.EVENT_ID)) { 20949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 20959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.extendedPropertiesInsert(values); 20987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Calendar.ExtendedProperties.EVENT_ID)); 21007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 21039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 21049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 21059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 21069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 21096db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2110315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 21117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 21149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 21179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2123e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2124e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Do some validation on event data before inserting. 2125e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * In particular make sure dtend, duration, etc make sense for 2126e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * the type of event (regular, recurrence, exception). Remove 2127e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * any unexpected fields. 2128e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 2129e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues to insert 2130e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2131e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private void validateEventData(ContentValues values) { 2132e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2133e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2134e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2135e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2136e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT)); 2137e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2138e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2139e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2140e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2141e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2142e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2143e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is the recurrence rule 2144e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2145e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2146e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2147e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 2148e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2149e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence: " + values); 2150e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2151e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2152e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_EVENT); 2153e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 2154e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2155e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2156e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2157e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2158e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2159e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2160e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2161e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2162e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2163e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2164e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 2165e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2166e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence exception: " + values); 2167e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2168e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2169e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2170e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2171e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2172e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2173e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2174e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2175e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2176e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2177e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2178e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2179e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 2180e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2181e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for event: " + values); 2182e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2183e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2184e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2185e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2186e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2187e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 21887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private void setEventDirty(int eventId) { 2189636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.execSQL("UPDATE Events SET _sync_dirty=1 where _id=?", new Integer[] {eventId}); 21907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 21929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 21939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 21949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 21959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 21969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 21979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2198f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2199f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2200f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2201f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2202f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2203f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 22049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 22059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 22069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 22079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 22099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 22109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 22119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 22129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 22139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2214f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2215f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2216f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 22179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 22189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 22209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 22229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 22239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 22269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 22309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 22319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 22329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 22339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 22349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 22359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 22369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 22389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 22399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 22409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 22419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 22429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 22439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 22449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 22459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 22469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 22479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 22499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 22509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 22519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 22559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 22569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 22579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 22589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 22599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 22609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 22629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 22639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 22649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 22669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 22679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 22689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 22699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 22749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 22759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2276f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2277f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2278f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 22799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 22809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 22829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 22959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2298f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2299f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2300f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 23119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 23159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 23169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 23189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 23219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 23229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 23239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 23249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 23339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 23419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2342636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.update("Events", values, "_id=?", new String[] {String.valueOf(eventId)}); 23439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 23469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the instances table when an event is added or updated. 23479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 23489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 23499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param newEvent true if the event is new. 23509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 23519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 23529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateInstancesLocked(ContentValues values, 23539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 23549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean newEvent, 23559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 23569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If there are no expanded Instances, then return. 23589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 23599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (fields.maxInstance == 0) { 23609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 23649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis == null) { 23659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (newEvent) { 23669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // must be present for a new event. 23679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART missing."); 23689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2369f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 2370f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Missing DTSTART. No need to update instance."); 2371f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 23769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!newEvent) { 23799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Want to do this for regular event, recurrence, or exception. 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // For recurrence or exception, more deletion may happen below if we 23819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do an instance expansion. This deletion will suffice if the exception 23829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // is moved outside the window, for instance. 2383636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.delete("Instances", "event_id=?", new String[] {String.valueOf(rowId)}); 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (isRecurrenceEvent(values)) { 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The recurrence or exception needs to be (re-)expanded if: 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Exception or recurrence that falls inside window 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean insideWindow = dtstartMillis <= fields.maxInstance && 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff (lastDateMillis == null || lastDateMillis >= fields.minInstance); 23919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Exception that affects instance inside window 23929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // These conditions match the query in getEntries 23939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // See getEntries comment for explanation of subtracting 1 week. 23949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean affectsWindow = originalInstanceTime != null && 23959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime <= fields.maxInstance && 23969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime >= fields.minInstance - MAX_ASSUMED_DURATION; 23979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (insideWindow || affectsWindow) { 23989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateRecurrenceInstancesLocked(values, rowId, db); 23999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: an exception creation or update could be optimized by 24019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // updating just the affected instances, instead of regenerating 24029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the recurrence. 24039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 24049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 24079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis == null) { 24089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = dtstartMillis; 24099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if the event is in the expanded range, insert 24129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // into the instances table. 24139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: deal with durations. currently, durations are only used in 24149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences. 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis <= fields.maxInstance && dtendMillis >= fields.minInstance) { 24179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues instanceValues = new ContentValues(); 24189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.EVENT_ID, rowId); 24199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.BEGIN, dtstartMillis); 24209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.END, dtendMillis); 24219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 24239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 24249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 24259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 24269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the timezone-dependent fields. 24299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local = new Time(); 24309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 24319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = Time.TIMEZONE_UTC; 24329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 24339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = fields.timezone; 24349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, local, instanceValues); 24379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.instancesInsert(instanceValues); 24389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Determines the recurrence entries associated with a particular recurrence. 24439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This set is the base recurrence and any exception. 24449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Normally the entries are indicated by the sync id of the base recurrence 24469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * (which is the originalEvent in the exceptions). 24479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * However, a complication is that a recurrence may not yet have a sync id. 24489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * In that case, the recurrence is specified by the rowId. 24499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param recurrenceSyncId The sync id of the base recurrence, or null. 24519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The row id of the base recurrence. 24529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return the relevant entries. 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getRelevantRecurrenceEntries(String recurrenceSyncId, long rowId) { 24559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 24569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 24589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 2459636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String selectionArgs[]; 24609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 2461636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_id =?"; 24629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2463636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {String.valueOf(rowId)}; 24649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 2465636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_sync_id = ? OR originalEvent = ?"; 24669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2467636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {recurrenceSyncId, recurrenceSyncId}; 24689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 24709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Retrieving events to expand: " + qb.toString()); 24719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2473636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return qb.query(mDb, EXPAND_COLUMNS, null /* selection */, selectionArgs, 24747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* groupBy */, null /* having */, null /* sortOrder */); 24759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Do incremental Instances update of a recurrence or recurrence exception. 24799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method does performInstanceExpansion on just the modified recurrence, 24819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid the overhead of recomputing the entire instance table. 24829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 24869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateRecurrenceInstancesLocked(ContentValues values, 24889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 24899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 24909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 2491315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 24929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 2493315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String recurrenceSyncId; 24949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null) { 24959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = originalEvent; 24969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 24979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the recurrence's sync id from the database 24989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = DatabaseUtils.stringForQuery(db, "SELECT _sync_id FROM Events" 2499636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff + " WHERE _id=?", new String[] {String.valueOf(rowId)}); 25009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrenceSyncId is the _sync_id of the underlying recurrence 25029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the recurrence hasn't gone to the server, it will be null. 25039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to clear out old instances 25059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating updating a recurrence that hasn't gone to the server. 25079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to delete based on row id 25089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 25099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 25109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 25119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._id =?)"; 25129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{"" + rowId}); 25139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating or modifying a recurrence or exception. 25159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Delete instances for recurrence (_sync_id = recurrenceSyncId) 25169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // and all exceptions (originalEvent = recurrenceSyncId) 25179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 25189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 25199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 25209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._sync_id =?" 25219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " OR Events.originalEvent =?)"; 25229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{recurrenceSyncId, recurrenceSyncId}); 25239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do instance expansion 25269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getRelevantRecurrenceEntries(recurrenceSyncId, rowId); 25279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 2528315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio performInstanceExpansion(fields.minInstance, fields.maxInstance, instancesTimezone, 25299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries); 25309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 25319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 25329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 25339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 25389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 25409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 25429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 25439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 25449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 25459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 25469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 25479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 25489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 25519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 25539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 25549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 25569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 25579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 25589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 25599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 25659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 25669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 25679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 25689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 25699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2571f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2572f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2573f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2574f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2575f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2576f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2577f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio values.get(Calendar.Events.RRULE), e); 2578f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2579f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2580f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2582f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 25839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 25849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 25879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 25899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 25909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 25919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 25959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 25979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 25989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 25999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 26009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 26029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 26039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 26049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 26079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 26089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 26109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2612e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2613e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 2614e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues (in/out) 2615e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2616e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2617e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 26189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 26209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 26219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 26229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 26259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 26269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2627f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2628f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2629f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 26309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 26319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 26369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("event_id", eventId); 26389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 26489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 26499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 26509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 26539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 26549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 26559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 26569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 26579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtstart2445", time.format2445()); 26589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 26619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 26629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 26639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtend2445", time.format2445()); 26649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 26679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 26689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 26699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 26709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 26719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 26729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 26739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 26749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 26769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("originalInstanceTime2445", time.format2445()); 26779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 26839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("lastDate2445", time.format2445()); 26849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 26879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2690b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2691b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2692ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 26939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 2701dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 27039323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2704dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2705dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2706dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2707dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 27121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection = appendAccountToSelection(uri, selection); 27137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 27141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 27151ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", ID_ONLY_PROJECTION, 27161ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 27177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27191ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 27201ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 272110b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 272310b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 2724dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 27321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 27331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 27341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 27351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 27361ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 27371ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 27381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 273910b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 27401ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 27447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Attendees", selection, selectionArgs); 27457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 27467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Attendees", uri, selection, selectionArgs); 27477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 27509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27512fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 27522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 27532fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 27557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2756636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Attendees", "_id=?", new String[] {String.valueOf(id)}); 27577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 27582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Attendees", uri, null /* selection */, 27592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 27607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 27657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Reminders", selection, selectionArgs); 27667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 27677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Reminders", uri, selection, selectionArgs); 27687e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 27719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27722fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 27732fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 27742fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 27767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2777636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Reminders", "_id=?", new String[] {String.valueOf(id)}); 27787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 27792fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Reminders", uri, null /* selection */, 27802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 27812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27822fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27832fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 27842fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 27852fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 27862fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return mDb.delete("ExtendedProperties", selection, selectionArgs); 27872fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 27882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, selection, selectionArgs); 27892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 27922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 27932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 27942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 27952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27962fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 27972fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2798636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("ExtendedProperties", "_id=?", 2799636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 28002fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 28012fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, null /* selection */, 28022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 28069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("CalendarAlerts", selection, selectionArgs); 28097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 28107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("CalendarAlerts", uri, selection, selectionArgs); 28117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 28149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28152fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28162fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28172fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28182fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 28192fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 28209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2821636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("CalendarAlerts", "_id=?", new String[] {String.valueOf(id)}); 28229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 28247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot delete that URL: " + uri); 28259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 28269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder("_id="); 28279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 28289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 28299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 28309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 28319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 28329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 28349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 28359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2836595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 28377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteMatchingCalendars(selection); // TODO: handle in sync adapter 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 28406db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2841315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 28429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 28439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 28449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 284810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 28491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2850192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 28511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 28521ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 28531ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", EVENTS_PROJECTION, 2854192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank "_id=?", selectionArgs, 2855636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 28561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 28571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 28581ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 28591ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 28601ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 2861370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik String rRule = cursor.getString(EVENTS_RRULE_INDEX); 2862370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik boolean emptyRRule = TextUtils.isEmpty(rRule); 286348f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 2864370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik if (!emptySyncId && !emptyRRule) { 2865370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik // Delete exceptions to this event as well. 2866370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik mDb.delete("Events", "originalEvent=?", new String[] {syncId}); 28671ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 28681ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 28691ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 28701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 28711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 28721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 28731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 28741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String origEvent = cursor.getString(EVENTS_ORIGINAL_EVENT_INDEX); 28751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate) 28761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff || !TextUtils.isEmpty(origEvent)) { 28771ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 28781ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 28791ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 288048f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 288148f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 288248f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 2883192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.delete("Events", "_id=?", selectionArgs); 28841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 28851ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 28861b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 28871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 2888192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.update("Events", values, "_id=?", selectionArgs); 288902494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 289043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 289143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 289202494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio mDb.delete("Instances", "event_id=?", selectionArgs); 289302494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio mDb.delete("EventsRawTimes", "event_id=?", selectionArgs); 289402494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio mDb.delete("Reminders", "event_id=?", selectionArgs); 289502494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio mDb.delete("CalendarAlerts", "event_id=?", selectionArgs); 289602494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio mDb.delete("ExtendedProperties", "event_id=?", selectionArgs); 28971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 28981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 28991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 29001ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 29011ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 29021ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29038f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 290410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 290510b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 2906dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 290710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 29081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 29091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 29127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 29137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 29147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 29157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 29167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 29177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 29187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 29197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 29207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 29217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 29227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues values = new ContentValues(); 29237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, "1"); 29247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 29257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 29267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 29277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 29287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2929636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete(table, "_id=?", new String[] {String.valueOf(id)}); 2930636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", values, "_id=?", new String[] {String.valueOf(event_id)}); 29317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 29327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 29347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 29357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 29377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 29397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 29407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 29417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 29427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 29437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 29447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 29457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 29467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 29477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 29487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 29497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 29507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 29517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 29527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues dirtyValues = new ContentValues(); 29537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff dirtyValues.put(Events._SYNC_DIRTY, "1"); 29547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 29557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 29567e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 29577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 29587e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2959636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update(table, values, "_id=?", new String[] {String.valueOf(id)}); 2960636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", dirtyValues, "_id=?", new String[] {String.valueOf(event_id)}); 29617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 29627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 29647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 29657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 29677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29687e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private int deleteMatchingCalendars(String where) { 29709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 29719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 29729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 29739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = mDb.query("Calendars", sCalendarsIdProjection, where, 29757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* selectionArgs */, null /* groupBy */, 29767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 29779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 29789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 29799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 29819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 29829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 29839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 29849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 29869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 29879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29882cc859cab85391a240b9c3f28c935d919c8ceb8cKen Shirriff return mDb.delete("Calendars", where, null /* whereArgs */); 29899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: call calculateLastDate()! 29929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 29939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 2994b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 2995ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 29969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 29979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 30009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 30019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 300343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS 3004315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio && match != EVENTS && match != CALENDARS && match != PROVIDER_PROPERTIES) { 30059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException( 30069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff "WHERE based updates not supported"); 30079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 30099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 30109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 30119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 30129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 30149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 3015dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 3016dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 30179323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3018dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3019dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3020dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 30219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 302343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 30249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 30259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 302643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 302743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 302843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null) { 302943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio throw new UnsupportedOperationException("Selection not permitted for " 303043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio + uri); 303143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 303243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 303343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 303443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 303543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 303643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 303743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 303843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 303943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null && selection.startsWith("_id=")) { 304043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 304143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 304243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 304343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 304443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio return mDb.update("Calendars", values, selection, selectionArgs); 304543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 304643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 304743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 304843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio values.put(Calendars._SYNC_DIRTY, 1); 30492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 30519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 30529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 30539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3055636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Calendars", values, "_id=?", 3056636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 30579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30583ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3059d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 3060d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang if (values.containsKey(Calendars.SELECTED)) { 3061d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3062d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3063d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3064d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3065d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang scheduleNextAlarm(false); 3066d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 30673ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3068dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 30693ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 30703ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 30729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 30749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 30759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = 0; 30777e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (match == EVENTS_ID) { 30787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = ContentUris.parseId(uri); 3079a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else if (callerIsSyncAdapter) { 308043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: same remark as for CALENDARS/CALENDARS_ID case as this is not 308143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // sufficient to deal with all the "_id" case in selection 3082a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff if (selection != null && selection.startsWith("_id=")) { 30837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // The ContentProviderOperation generates an _id=n string instead of 30847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // adding the id to the URL, so parse that out here. 30857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = Long.parseLong(selection.substring(4)); 3086a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else { 3087a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // Sync adapter Events operation affects just Events table, not associated 3088a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // tables. 3089646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, values)) { 3090f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3091f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: Caller is sync adapter. " + 3092f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3093f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3094646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3095a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff return mDb.update("Events", values, selection, selectionArgs); 3096a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } 30977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3098a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 30997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 31017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 31027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Disallow updating the attendee status in the Events 31049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // table. In the future, we could support this but we 31059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // would have to query and update the attendees table 31069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to keep the values consistent. 31079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 31089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 31099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.SELF_ATTENDEE_STATUS 31109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 31119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // TODO: should we allow this? 31147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (values.containsKey(Events.HTML_URI) && !callerIsSyncAdapter) { 31159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 31169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.HTML_URI 31179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 31189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3119e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 3120e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // TODO: should extend validateEventData to work with updates and call it here 3121e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 31229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 3123f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3124f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not update event."); 3125f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 31269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 31279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3128646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we pass in a uri with the id appended to fixAllDayTime 3129646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Uri allDayUri; 3130646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 1) { 3131646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = ContentUris.withAppendedId(uri, id); 3132646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 3133646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = uri; 3134646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3135646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(allDayUri, updatedValues)) { 3136f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3137f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: " + 3138f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3139f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3140646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 31419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3142636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Events", updatedValues, "_id=?", 3143636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (result > 0) { 31459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 31469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, false /* not a new event */, mDb); 31479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTSTART)) { 31499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The start time of the event changed, so run the 31509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarm scheduler. 31519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 31529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing event"); 31539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 31559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31563ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3157dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 31589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31593ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 31609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 31619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 31632fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 31679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 31689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3171636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("Attendees", values, "_id=?", 317283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 31737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 31742fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("Attendees", values, uri, null /* selection */, 31752fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 31767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 31792fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31822fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 31832fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 31849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3185636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("CalendarAlerts", values, "_id=?", 3186636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 31892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 31902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 31919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDb.update("CalendarAlerts", values, selection, selectionArgs); 31929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 31942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31962fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3199636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff count = mDb.update("Reminders", values, "_id=?", 320083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 32017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 32022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff count = updateInTable("Reminders", values, uri, null /* selection */, 32032fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 32047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 32069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 32079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 32089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 32099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 32109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 32127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 32139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32142fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 32152fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 32162fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 32172fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 32187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 32197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3220636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("ExtendedProperties", values, "_id=?", 3221636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 32227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 32232fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("ExtendedProperties", values, uri, null /* selection */, 32242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 32257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 322783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 322883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 322983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 323083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(false); 323183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 323283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 323383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 323483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(true); 323583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 323683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 32379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3238315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 3239315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (selection == null) { 3240315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection cannot be null for " + uri); 3241315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3242315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 3243315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 3244315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3245315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3246315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 3247315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3248315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 3249315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 3250315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 3251315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3252315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3253315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 3254315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 3255315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3256315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 3257315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 3258315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio int result = mDb.update("CalendarCache", values, selection, selectionArgs); 3259315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3260315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 3261315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone type is set to "home", set the Instances timezone to the previous 3262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone type is set to "auto", set the Instances timezone to the current 3263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // device one 3264315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if the timezone Instances is set AND if we are in "home" timezone type, then 3265315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // save the timezone Instance into "previous" too 3266315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 3267315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 3268315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 3269315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 3270315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 3271315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 3272315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 3273315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 3274315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 3275315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 3276315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 3277315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3278315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 3279d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 3280315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 3281315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3282d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3285315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 3286315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 3287315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 3288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 3289315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 3290315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3291d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3292315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3293315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3294315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3295315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3296315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 3297315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 3298315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 3299315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 3300315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 3301315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 3302315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 3303315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 3304d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 3305315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 3306315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 3307315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3308d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3309315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3310315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3311315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3312315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3313315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 3314315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3315315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 33169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 33179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 33189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3321595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) { 3322b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3323b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_NAME); 3324b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3325b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_TYPE); 3326595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 3327595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere(Calendar.Calendars._SYNC_ACCOUNT + "=" 3328595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3329595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 3330595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 3331595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 3332595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere("1"); // I.e. always true 3333595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3334595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3335595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 33369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 3337b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3338b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_NAME); 3339b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3340b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio Calendar.EventsEntity.ACCOUNT_TYPE); 33419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 33429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder(Calendar.Calendars._SYNC_ACCOUNT + "=" 33439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 33449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 33459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 33469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 33479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 33489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 33499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 33509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selectionSb.toString(); 33529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 33539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 33549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 33589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 33599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 33609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 33619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] {Calendars._SYNC_ACCOUNT, Calendars._SYNC_ACCOUNT_TYPE, 33621b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio Calendars.SYNC1, Calendars.SYNC_EVENTS}, 33639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 33649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 33659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 33669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 33689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 33699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 3370ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 33719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3372ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 3373ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 3374ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 3375ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 3376ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 3377ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 3378ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 33799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 33809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 33819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33849535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 33859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 3386f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3387f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 3388f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 3389f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 33909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 33919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33939535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 33949535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 33959535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 33969535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 33979535627bf6295cd94447beb83e1aac41f50c3600Erik } 33989535627bf6295cd94447beb83e1aac41f50c3600Erik 33999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 34009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 34019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 34029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 34059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 34069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 34079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this needed 34109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// @Override 34119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// public void onSyncStop(SyncContext context, boolean success) { 34129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// super.onSyncStop(context, success); 34139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// if (Log.isLoggable(TAG, Log.DEBUG)) { 34149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// Log.d(TAG, "onSyncStop() success: " + success); 34159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 34169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// scheduleNextAlarm(false /* do not remove alarms */); 34179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// triggerAppWidgetUpdate(-1); 34189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 34199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Retrieve and cache the alarm manager */ 34219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager getAlarmManager() { 34229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff synchronized(mAlarmLock) { 34239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (mAlarmManager == null) { 34249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 34259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (context == null) { 3426f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 3427f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "getAlarmManager() cannot get Context"); 3428f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 34299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 34309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Object service = context.getSystemService(Context.ALARM_SERVICE); 34329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mAlarmManager = (AlarmManager) service; 34339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mAlarmManager; 34359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarmCheck(long triggerTime) { 34399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 34409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager == null) { 3441f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 3442f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "scheduleNextAlarmCheck() cannot get AlarmManager"); 3443f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 34449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 34459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 34479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Intent intent = new Intent(CalendarReceiver.SCHEDULE); 34489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff intent.setClass(context, CalendarReceiver.class); 34499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff PendingIntent pending = PendingIntent.getBroadcast(context, 34509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_NO_CREATE); 34519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (pending != null) { 34529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Cancel any previous alarms that do the same thing. 34539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.cancel(pending); 34549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff pending = PendingIntent.getBroadcast(context, 34569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 34579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 34599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 34609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(triggerTime); 34619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 34629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "scheduleNextAlarmCheck at: " + triggerTime + timeStr); 34639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.set(AlarmManager.RTC_WAKEUP, triggerTime, pending); 34669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 34699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs the alarm scheduler in a background thread. 34709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 34719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarm(boolean removeAlarms) { 34729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new AlarmScheduler(removeAlarms); 34739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 34749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 34779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread and schedules an alarm for 34789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the next calendar event, if necessary. 34799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 34809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void runScheduleNextAlarm(boolean removeAlarms) { 34819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 34829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.beginTransaction(); 34839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 34849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (removeAlarms) { 34859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff removeScheduledAlarmsLocked(db); 34869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarmLocked(db); 34889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.setTransactionSuccessful(); 34899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 34909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.endTransaction(); 34919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 34959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method looks at the 24-hour window from now for any events that it 34969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * needs to schedule. This method runs within a database transaction. It 34979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * also runs in a background thread. 34989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 34999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The CalendarProvider2 keeps track of which alarms it has already scheduled 35009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid scheduling them more than once and for debugging problems with 35019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms. It stores this knowledge in a database table called CalendarAlerts 35029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * which persists across reboots. But the actual alarm list is in memory 35039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and disappears if the phone loses power. To avoid missing an alarm, we 35049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * clear the entries in the CalendarAlerts table when we start up the 35059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * CalendarProvider2. 35069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 35079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Scheduling an alarm multiple times is not tragic -- we filter out the 35089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * extra ones when we receive them. But we still need to keep track of the 35099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled alarms. The main reason is that we need to prevent multiple 35109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * notifications for the same alarm (on the receive side) in case we 35119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * accidentally schedule the same alarm multiple times. We don't have 35129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * visibility into the system's alarm list so we can never know for sure if 35139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we have already scheduled an alarm and it's better to err on scheduling 35149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * an alarm twice rather than missing an alarm. Another reason we keep 35159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * track of scheduled alarms in a database table is that it makes it easy to 35169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * run an SQL query to find the next reminder that we haven't scheduled. 35179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 35189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 35199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 35209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void scheduleNextAlarmLocked(SQLiteDatabase db) { 3521b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang Time time = new Time(); 35229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager alarmManager = getAlarmManager(); 35239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmManager == null) { 3524f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 3525f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Failed to find the AlarmManager. Could not schedule the next alarm!"); 3526f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 35279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 35289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long currentMillis = System.currentTimeMillis(); 35319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long start = currentMillis - SCHEDULE_ALARM_SLACK; 35329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long end = start + (24 * 60 * 60 * 1000); 35339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = getContext().getContentResolver(); 35349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 35359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(start); 35369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 35379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "runScheduleNextAlarm() start search: " + startTimeStr); 35389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35408f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Delete rows in CalendarAlert where the corresponding Instance or 35418f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Reminder no longer exist. 35428f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Also clear old alarms but keep alarms around for a while to prevent 35439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // multiple alerts for the same reminder. The "clearUpToTime' 35449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should be further in the past than the point in time where 35459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we start searching for events (the "start" variable defined above). 35468f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff String selectArg[] = new String[] { 35478f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Long.toString(currentMillis - CLEAR_OLD_ALARM_THRESHOLD) 35488f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff }; 35498f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 35508f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff int rowsDeleted = 35518f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff db.delete(CalendarAlerts.TABLE_NAME, INVALID_CALENDARALERTS_SELECTOR, selectArg); 35529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long nextAlarmTime = end; 35548f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long tmpAlarmTime = CalendarAlerts.findNextAlarmTime(cr, currentMillis); 35558f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (tmpAlarmTime != -1 && tmpAlarmTime < nextAlarmTime) { 35568f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff nextAlarmTime = tmpAlarmTime; 35579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Extract events from the database sorted by alarm time. The 35609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // alarm times are computed from Instances.begin (whose units 35619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // are milliseconds) and Reminders.minutes (whose units are 35629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // minutes). 35639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 35649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events whose end time is already in the past. 35659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events alarms that we have already scheduled. 35669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 35679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 1: we can add support for the case where Reminders.minutes 35689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // equals -1 to mean use Calendars.minutes by adding a UNION for 35699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that case where the two halves restrict the WHERE clause on 35709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders.minutes != -1 and Reminders.minutes = 1, respectively. 35719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 35729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 2: we have to name "myAlarmTime" different from the 35739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "alarmTime" column in CalendarAlerts because otherwise the 35749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query won't find multiple alarms for the same event. 3575156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // 3576156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // The CAST is needed in the query because otherwise the expression 3577156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will be untyped and sqlite3's manifest typing will not convert the 3578156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // string query parameter to an int in myAlarmtime>=?, so the comparison 3579156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will fail. This could be simplified if bug 2464440 is resolved. 3580b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3581b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang time.setToNow(); 3582b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang time.normalize(false); 3583b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang long localOffset = time.gmtoff * 1000; 3584b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3585b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String allDayOffset = " -(" + localOffset + ") "; 3586b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String subQueryPrefix = "SELECT " + Instances.BEGIN; 3587b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String subQuerySuffix = " -(" + Reminders.MINUTES + "*" + 3588b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + DateUtils.MINUTE_IN_MILLIS + ")" 3589b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AS myAlarmTime" + "," + Tables.INSTANCES 3590d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "." + Instances.EVENT_ID + " AS eventId" 3591d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Instances.BEGIN + "," + Instances.END 3592d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Instances.TITLE + "," + Instances.ALL_DAY 3593d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "," + Reminders.METHOD + "," + Reminders.MINUTES 3594d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " FROM " + Tables.INSTANCES 3595d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " INNER JOIN " + Views.EVENTS 3596d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ON (" + Views.EVENTS + "." + Events._ID 3597d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "=" + Tables.INSTANCES + "." + Instances.EVENT_ID + ")" 3598d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " INNER JOIN " + Tables.REMINDERS 3599d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ON (" + Tables.INSTANCES + "." + Instances.EVENT_ID 3600d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + ")" 3601d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " WHERE " + Calendars.SELECTED + "=1" 3602156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime>=CAST(? AS INT)" 3603156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime<=CAST(? AS INT)" 3604d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " AND " + Instances.END + ">=?" 3605b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Reminders.METHOD + "=" + Reminders.METHOD_ALERT; 3606b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3607b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we query separately for all day events to convert to local time from UTC 3608b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we need to /subtract/ the offset to get the correct resulting local time 3609b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String allDayQuery = subQueryPrefix + allDayOffset + subQuerySuffix 3610b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Instances.ALL_DAY + "=1"; 3611b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String nonAllDayQuery = subQueryPrefix + subQuerySuffix 3612b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND " + Instances.ALL_DAY + "=0"; 3613b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3614b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // we use UNION ALL because we are guaranteed to have no dupes between 3615b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // the two queries, and it is less expensive 3616b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String query = "SELECT *" 3617b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " FROM (" + allDayQuery + " UNION ALL " + nonAllDayQuery + ")" 3618b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // avoid rescheduling existing alarms 3619b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " WHERE 0=(SELECT count(*) from " + Tables.CALENDAR_ALERTS + " CA" 3620b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " WHERE CA." + CalendarAlerts.EVENT_ID + "=eventId" 3621b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND CA." + CalendarAlerts.BEGIN + "=" + Instances.BEGIN 3622b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang + " AND CA." + CalendarAlerts.ALARM_TIME + "=myAlarmTime)" 3623d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang + " ORDER BY myAlarmTime," + Instances.BEGIN + "," + Instances.TITLE; 36249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3625b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String queryParams[] = new String[] { String.valueOf(start), String.valueOf(nextAlarmTime), 3626b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String.valueOf(currentMillis), String.valueOf(start), String.valueOf(nextAlarmTime), 3627b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang String.valueOf(currentMillis) }; 3628b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang 3629315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 3630315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone = mCalendarCache.readTimezoneType().equals( 3631315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.TIMEZONE_TYPE_HOME); 3632b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang // expand this range by a day on either end to account for all day events 3633b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang acquireInstanceRangeLocked(start - DateUtils.DAY_IN_MILLIS, 3634b222a85a892be92fe380c36abeaea79aa8f160ddMason Tang end + DateUtils.DAY_IN_MILLIS, 3635d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* don't use minimum expansion windows */, 3636315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 3637315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 3638315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone); 36399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 36409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3641156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff cursor = db.rawQuery(query, queryParams); 36429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36438f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int beginIndex = cursor.getColumnIndex(Instances.BEGIN); 36448f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int endIndex = cursor.getColumnIndex(Instances.END); 36458f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int eventIdIndex = cursor.getColumnIndex("eventId"); 36468f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int alarmTimeIndex = cursor.getColumnIndex("myAlarmTime"); 36478f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutesIndex = cursor.getColumnIndex(Reminders.MINUTES); 36489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 36509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(nextAlarmTime); 36519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String alarmTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 36528f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "cursor results: " + cursor.getCount() + " nextAlarmTime: " 36538f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + alarmTimeStr); 36549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (cursor.moveToNext()) { 36579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule all alarms whose alarm time is as early as any 36589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // scheduled alarm. For example, if the earliest alarm is at 36599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 1pm, then we will schedule all alarms that occur at 1pm 36609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // but no alarms that occur later than 1pm. 36619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Actually, we allow alarms up to a minute later to also 36629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be scheduled so that we don't have to check immediately 36639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // again after an event alarm goes off. 36648f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long alarmTime = cursor.getLong(alarmTimeIndex); 36658f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long eventId = cursor.getLong(eventIdIndex); 36668f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutes = cursor.getInt(minutesIndex); 36678f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long startTime = cursor.getLong(beginIndex); 36688f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long endTime = cursor.getLong(endIndex); 36699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 36719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(alarmTime); 36729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); 36739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(startTime); 36749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 36758f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 36768f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, " looking at id: " + eventId + " " + startTime + startTimeStr 36778f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " alarm: " + alarmTime + schedTime); 36789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmTime < nextAlarmTime) { 36819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff nextAlarmTime = alarmTime; 36829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (alarmTime > 36831edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff nextAlarmTime + DateUtils.MINUTE_IN_MILLIS) { 36849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This event alarm (and all later ones) will be scheduled 36859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // later. 36868f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 36878f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "This event alarm (and all later ones) will be scheduled later"); 36888f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 36899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 36909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Avoid an SQLiteContraintException by checking if this alarm 36939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // already exists in the table. 36949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (CalendarAlerts.alarmExists(cr, eventId, startTime, alarmTime)) { 36959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 36969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int titleIndex = cursor.getColumnIndex(Events.TITLE); 36979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String title = cursor.getString(titleIndex); 36989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, " alarm exists for id: " + eventId + " " + title); 36999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 37019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Insert this alarm into the CalendarAlerts table 37049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Uri uri = CalendarAlerts.insert(cr, eventId, startTime, 37059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endTime, alarmTime, minutes); 37069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (uri == null) { 3707f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 3708f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "runScheduleNextAlarm() insert into " 3709f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "CalendarAlerts table failed"); 3710f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 37119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 37129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37148f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, alarmTime); 37159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 37179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 37189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 37199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37228f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Refresh notification bar 37238f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (rowsDeleted > 0) { 37248f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, currentMillis); 37258f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 37268f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 37279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we scheduled an event alarm, then schedule the next alarm check 37289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for one minute past that alarm. Otherwise, if there were no 37299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarms scheduled, then check again in 24 hours. If a new 37309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event is inserted before the next alarm check, then this method 37319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will be run again when the new event is inserted. 37329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (nextAlarmTime != Long.MAX_VALUE) { 37331edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(nextAlarmTime + DateUtils.MINUTE_IN_MILLIS); 37349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 37351edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(currentMillis + DateUtils.DAY_IN_MILLIS); 37369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 37409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Removes the entries in the CalendarAlerts table for alarms that we have 37419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled but that have not fired yet. We do this to ensure that we 37429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * don't miss an alarm. The CalendarAlerts table keeps track of the 37439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms that we have scheduled but the actual alarm list is in memory 37449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and will be cleared if the phone reboots. 37459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 37469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We don't need to remove entries that have already fired, and in fact 37479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we should not remove them because we need to display the notifications 37489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * until the user dismisses them. 37499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 37509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We could remove entries that have fired and been dismissed, but we leave 37519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * them around for a while because it makes it easier to debug problems. 37529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Entries that are old enough will be cleaned up later when we schedule 37539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * new alarms. 37549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 37559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void removeScheduledAlarmsLocked(SQLiteDatabase db) { 37569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 37579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "removing scheduled alarms"); 37589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete(CalendarAlerts.TABLE_NAME, 37609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.STATE + "=" + CalendarAlerts.SCHEDULED, null /* whereArgs */); 37619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3763a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3764a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3765a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3766a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 3767dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 3768dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * @param whether or not the update is being triggered by a sync 3769a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3770dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 3771dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 3772dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 3773a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3774a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3775a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3776a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3777a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3778a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 3779a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 3780a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 3781a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3782a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 3783a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3784a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * @param the ID of the event that changed, or -1 for no specific event 3785dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * @param whether or not the update is being triggered by a sync 3786a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3787dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 3788dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 3789a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 3790a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 3791a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 3792a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 3793a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 3794dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 3795dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 3796dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 3797dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 3798dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 3799dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 3800dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 3801dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 3802dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 3803dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 3804dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 3805dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 3806dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 3807dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 3808dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 3809dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 3810dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 3811a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 3812dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 3813a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3814a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3815a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3816a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 3817a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 3818a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #sendUpdateNotification()} instead. 3819a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3820a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * @see #sendUpdateNotification() 3821a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3822a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 3823a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 3824dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Calendar.CONTENT_URI); 3825f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 3826f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 3827f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3828a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang getContext().sendBroadcast(intent, null); 3829a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3830a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 38319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sEventsTable = "Events"; 38329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sAttendeesTable = "Attendees"; 38339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sRemindersTable = "Reminders"; 38349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sCalendarAlertsTable = "CalendarAlerts"; 38359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sExtendedPropertiesTable = "ExtendedProperties"; 38369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 38389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 38399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 38409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int DELETED_EVENTS = 4; 38419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS = 5; 38429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_ID = 6; 38439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES = 7; 38449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES_ID = 8; 38459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS = 9; 38469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS_ID = 10; 38479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES = 11; 38489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES_ID = 12; 38499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS = 13; 38509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_ID = 14; 38519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_BY_INSTANCE = 15; 38526db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int INSTANCES_BY_DAY = 16; 38536db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE = 17; 38546db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE_ID = 18; 38556db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES = 19; 38566db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES_ID = 20; 38576db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_DAYS = 21; 385883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM = 22; 385983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM_REMOVE = 23; 386048587d3291c4db7f0942e1bff55b88cfa7764ba0Erik private static final int TIME = 24; 386143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio private static final int CALENDAR_ENTITIES = 25; 386243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio private static final int CALENDAR_ENTITIES_ID = 26; 386381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final int INSTANCES_SEARCH = 27; 386481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final int INSTANCES_SEARCH_BY_DAY = 28; 3865315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final int PROVIDER_PROPERTIES = 29; 38669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 38689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 38699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sEventsProjectionMap; 387019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 38719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 38729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 38739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 3874315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 38759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 3877b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/when/*/*", INSTANCES); 3878b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 387981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sUriMatcher.addURI(Calendar.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 388081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sUriMatcher.addURI(Calendar.AUTHORITY, "instances/searchbyday/*/*/*", 388181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 3882b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 3883b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events", EVENTS); 3884b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events/#", EVENTS_ID); 3885b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities", EVENT_ENTITIES); 3886b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 3887b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars", CALENDARS); 3888b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars/#", CALENDARS_ID); 388943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 389043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 3891b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "deleted_events", DELETED_EVENTS); 3892b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees", ATTENDEES); 3893b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees/#", ATTENDEES_ID); 3894b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders", REMINDERS); 3895b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders/#", REMINDERS_ID); 3896b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 3897b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties/#", EXTENDED_PROPERTIES_ID); 3898b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 3899b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 3900b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/by_instance", 3901b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 3902c4e53191b570e09959c5723f4d253977ba48f2d0Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate", SYNCSTATE); 390383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 390483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_PATH, SCHEDULE_ALARM); 390583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 390648587d3291c4db7f0942e1bff55b88cfa7764ba0Erik sUriMatcher.addURI(Calendar.AUTHORITY, "time/#", TIME); 3907997e2e5cb006682bc1a82441304994b458d9745dErik sUriMatcher.addURI(Calendar.AUTHORITY, "time", TIME); 3908315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "properties", PROVIDER_PROPERTIES); 39099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 39119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 39129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HTML_URI, "htmlUri"); 39139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TITLE, "title"); 39149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 39159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DESCRIPTION, "description"); 39169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.STATUS, "eventStatus"); 39179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 39189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 39199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTSTART, "dtstart"); 39209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTEND, "dtend"); 39219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 39229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DURATION, "duration"); 39239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ALL_DAY, "allDay"); 39249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.VISIBILITY, "visibility"); 39259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TRANSPARENCY, "transparency"); 39269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 39279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 39289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RRULE, "rrule"); 39299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RDATE, "rdate"); 39309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXRULE, "exrule"); 39319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXDATE, "exdate"); 39329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 39339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 39349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 39359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.LAST_DATE, "lastDate"); 39369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 39379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 39389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 39399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 39409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 39419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORGANIZER, "organizer"); 39421b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventsProjectionMap.put(Events.DELETED, "deleted"); 39439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3944e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 39451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 39461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 39471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 39489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 3949982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.COLOR, "color"); 3950982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.ACCESS_LEVEL, "access_level"); 3951982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.SELECTED, "selected"); 39521b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventsProjectionMap.put(Calendars.SYNC1, "sync1"); 39539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.TIMEZONE, "timezone"); 39549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, "ownerAccount"); 39559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3956982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 3957e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 3958e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 3959982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3960e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3961982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 39621ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._ID, "_id"); 39631ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ID, "_sync_id"); 39641ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_VERSION, "_sync_version"); 39651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_TIME, "_sync_time"); 3966c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventsProjectionMap.put(Events._SYNC_DATA, "_sync_local_id"); 39671ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_DIRTY, "_sync_dirty"); 39681ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT, "_sync_account"); 39699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT_TYPE, 39701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff "_sync_account_type"); 39719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 397246f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 397319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HTML_URI, "htmlUri"); 397419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TITLE, "title"); 397519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DESCRIPTION, "description"); 397619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 397719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.STATUS, "eventStatus"); 397819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 397919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 398019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTSTART, "dtstart"); 398119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTEND, "dtend"); 398219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DURATION, "duration"); 398319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 398419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ALL_DAY, "allDay"); 398519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.VISIBILITY, "visibility"); 398619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TRANSPARENCY, "transparency"); 398719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 398819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 398919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RRULE, "rrule"); 399019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RDATE, "rdate"); 399119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXRULE, "exrule"); 399219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXDATE, "exdate"); 399319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 399419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 399519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 399619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.LAST_DATE, "lastDate"); 399719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 399819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 399919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 400019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 400119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 400219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORGANIZER, "organizer"); 40031b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventEntitiesProjectionMap.put(Events.DELETED, "deleted"); 400419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 400519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 4006c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventEntitiesProjectionMap.put(Events._SYNC_DATA, Events._SYNC_DATA); 400719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_VERSION, Events._SYNC_VERSION); 400819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_DIRTY, Events._SYNC_DIRTY); 40091b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sEventEntitiesProjectionMap.put(Calendars.SYNC1, Calendars.SYNC1); 401019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 40119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 40121b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 40139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 40149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 40159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 40169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 40179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 40189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 40199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 40209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 40219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 40239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 40249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 40259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 40269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 40279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 40289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 40299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 40309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 40329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 40339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 40349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 40359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 40369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 40389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 40399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 40409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 40419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 40429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 40439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 40449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4045315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4046315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4047315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4048315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4049315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 40509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 40539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make sure that there are no entries for accounts that no longer 40549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * exist. We are overriding this since we need to delete from the 40559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 40567e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 40577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 40589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 40599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 4060ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4061ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4062ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4063ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4064ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4065ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 40669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 406746f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 406846f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 40699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 40709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 40719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 40729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 40749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 40769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 40779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (String table : new String[]{"Calendars"}) { 4079ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 40809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 40817cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Cursor c = mDb.rawQuery("SELECT DISTINCT " + 40827cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT + 40837cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 40847cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT_TYPE + 40857cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 40869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 40879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c.getString(0) != null && c.getString(1) != null) { 40889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 40899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 40909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 40919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 40959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 4098f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 4099f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 4100f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 41019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 41027cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio mDb.execSQL("DELETE FROM Calendars" + 41037cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " WHERE " + 41047cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT + "= ? AND " + 41057cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Calendar.SyncColumns._SYNC_ACCOUNT_TYPE + "= ?", params); 41069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 41089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 41099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 41109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 41119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41123ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 41133ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 4114dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 41159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4117636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 4118636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 4119636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 4120636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 4121636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 4122636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 4123636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 4124636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 4125636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 4126636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 4127636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 4128636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 4129636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 4130636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 4131636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 4132636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 4133636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 4134636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 4135636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 41369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 4137