CalendarProvider2.java revision ab42ec67e77c398ac94ff1cf561fadd9f6b48dcb
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 209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.AlarmManager; 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.PendingIntent; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Debug; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 41f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglioimport android.pim.EventRecurrence; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.pim.RecurrenceSet; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar; 459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Attendees; 469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.CalendarAlerts; 479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Calendars; 489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Events; 499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Instances; 509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Reminders; 519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 521edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 53192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Config; 559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 57ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 60ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.Set; 649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * is defined in {@link android.provider.Calendar}. 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String TAG = "CalendarProvider2"; 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean PROFILE = false; 759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 768f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 778f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff private static final String INVALID_CALENDARALERTS_SELECTOR = 788f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff "_id IN (SELECT ca._id FROM CalendarAlerts AS ca" 798f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " LEFT OUTER JOIN Instances USING (event_id, begin, end)" 808f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " LEFT OUTER JOIN Reminders AS r ON" 818f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " (ca.event_id=r.event_id AND ca.minutes=r.minutes)" 828f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " WHERE Instances.begin ISNULL OR ca.alarmTime<?" 838f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " OR (r.minutes ISNULL AND ca.minutes<>0))"; 848f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 851ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 861ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_ORIGINAL_EVENT_INDEX = 3; 987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 1079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 112ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 1159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final Uri SYNCSTATE_CONTENT_URI = 1179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Uri.parse("content://syncstate/state"); 11883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // 11983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_URI runs scheduleNextAlarm(false) 12083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_REMOVE_URI runs scheduleNextAlarm(true) 12183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: use a service to schedule alarms rather than private URI 12283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_PATH = "schedule_alarms"; 12383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; 12483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_URI = 12583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_PATH); 12683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_REMOVE_URI = 12783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); 1289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // To determine if a recurrence exception originally overlapped the 1309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window, we need to assume a maximum duration, since we only know 1319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original start time. 1329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int MAX_ASSUMED_DURATION = 7*24*60*60*1000; 1339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class TimeRange { 1359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean allDay; 1389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesRange { 1419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public InstancesRange(long begin, long end) { 1459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.begin = begin; 1469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.end = end; 1479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesList 1519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends ArrayList<ContentValues> { 1529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class EventInstancesMap 1559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends HashMap<String, InstancesList> { 1569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void add(String syncId, ContentValues values) { 1579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff InstancesList instances = get(syncId); 1589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (instances == null) { 1599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances = new InstancesList(); 1609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff put(syncId, instances); 1619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances.add(values); 1639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A thread that runs in the background and schedules the next 1679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // calendar event alarm. 1689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class AlarmScheduler extends Thread { 1699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean mRemoveAlarms; 1709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public AlarmScheduler(boolean removeAlarms) { 1729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mRemoveAlarms = removeAlarms; 1739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 175192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank @Override 1769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 1779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 1799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff runScheduleNextAlarm(mRemoveAlarms); 1809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e) { 1819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "runScheduleNextAlarm() failed", e); 1829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 1879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We search backward in time for event reminders that we may have missed 1889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and schedule them if the event has not yet expired. The amount in 1899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the past to search backwards is controlled by this constant. It 1909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should be at least a few minutes to allow for an event that was 1919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * recently created on the web to make its way to the phone. Two hours 1929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * might seem like overkill, but it is useful in the case where the user 1939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * just crossed into a new timezone and might have just missed an alarm. 1949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1951edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff private static final long SCHEDULE_ALARM_SLACK = 2 * DateUtils.HOUR_IN_MILLIS; 1969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 1989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Alarms older than this threshold will be deleted from the CalendarAlerts 1999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. This should be at least a day because if the timezone is 2009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * wrong and the user corrects it we might delete good alarms that 2019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * appear to be old because the device time was incorrectly in the future. 2029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This threshold must also be larger than SCHEDULE_ALARM_SLACK. We add 2039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the SCHEDULE_ALARM_SLACK to ensure this. 2049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 2059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * To make it easier to find and debug problems with missed reminders, 2069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * set this to something greater than a day. 2079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long CLEAR_OLD_ALARM_THRESHOLD = 2091edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff 7 * DateUtils.DAY_IN_MILLIS + SCHEDULE_ALARM_SLACK; 2109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A lock for synchronizing access to fields that are shared 2129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // with the AlarmScheduler thread. 2139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Object mAlarmLock = new Object(); 2149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allocate the string constant once here instead of on the heap 2249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String CALENDAR_ID_SELECTION = "calendar_id=?"; 2259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sInstancesProjection = 2279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Instances.START_DAY, Instances.END_DAY, 2289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Instances.START_MINUTE, Instances.END_MINUTE, Instances.ALL_DAY }; 2299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager mAlarmManager; 2379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarAppWidgetProvider mAppWidgetProvider = CalendarAppWidgetProvider.getInstance(); 2399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 2429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 2449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 2469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 2479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 2499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 2519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 2629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 263ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 264ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Columns from the EventsRawTimes table 265ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 266ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public interface EventsRawTimesColumns 267ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio { 268ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 269ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The corresponding event id 270ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: INTEGER (long)</P> 271ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 272ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String EVENT_ID = "event_id"; 273ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 274ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 275ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant time the event starts 276ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 277ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 278ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String DTSTART_2445 = "dtstart2445"; 279ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 280ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 281ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant time the event ends 282ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 283ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 284ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String DTEND_2445 = "dtend2445"; 285ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 286ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 287ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant original instance time of the recurring event for which this 288ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * event is an exception. 289ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 290ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 291ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445"; 292ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 293ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 294ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant last date this event repeats on, or NULL if it never ends 295ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 296ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 297ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String LAST_DATE_2445 = "lastDate2445"; 298ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 299ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 3009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void verifyAccounts() { 3019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 3029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff onAccountsUpdated(AccountManager.get(getContext()).getAccounts()); 3039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 3069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 3089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 3099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 3139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 3149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 3159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff verifyAccounts(); 3179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 3199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 3209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 3229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 3239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 3249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Context c = getContext(); 3259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 3279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 3289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 3299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 3309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.registerReceiver(mIntentReceiver, filter); 3319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData = new MetaData(mDbHelper); 333ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 334ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 3359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 3369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 3389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 3419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 3429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 3439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * has changes. 3449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 3469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 3479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 3489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 3519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 3539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 3549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff doUpdateTimezoneDependentFields(); 3569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e) { 3579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 3589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Clear at least the in-memory data (and if possible the 3609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // database fields) to force a re-computation of Instances. 3619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData.clearInstanceRange(); 3629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e2) { 3639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "clearInstanceRange() also failed: " + e2); 3649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 3709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 3719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 3729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void doUpdateTimezoneDependentFields() { 374ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 375ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio doProcessEventRawTimes(null /* default current timezone*/, 376ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio TimeUtils.getTimeZoneDatabaseVersion()); 377ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 378ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (isSameTimezone()) { 3799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Even if the timezone hasn't changed, check for missed alarms. 3809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This code executes when the CalendarProvider2 is created and 3819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // helps to catch missed alarms when the Calendar process is 3829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // killed (because of low-memory conditions) and then restarted. 3839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 3849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 3859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 386ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 387ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 388ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 389ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected void doProcessEventRawTimes(String timezone, String timeZoneDatabaseVersion) { 390ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 391ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (mDb == null) { 392ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 393ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Cannot update Events table from EventsRawTimes table"); 394ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 395ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return; 396ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 397ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 398ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 399ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(timezone); 400ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 401ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cleanInstancesTable(); 402ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 403ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 404ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 405ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 406ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 407ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 408ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 409ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 410ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked(String timezone) { 411ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Cursor cursor = mDb.query("EventsRawTimes", 412ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio new String[] { EventsRawTimesColumns.EVENT_ID, 413ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio EventsRawTimesColumns.DTSTART_2445, 414ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio EventsRawTimesColumns.DTEND_2445} /* projection */, 415ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio null /* selection */, 416ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio null /* selection args */, 417ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio null /* group by */, 418ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio null /* having */, 419ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio null /* order by */ 420ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ); 421ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 422ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 423ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 424ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 425ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 426ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 427ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio timezone, 428ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 429ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 430ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 431ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 432ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 433ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 434ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 435ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 436ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 437ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 438ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 439ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v( TAG, "Cannot parse null RFC2445 date"); 440ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 441ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 442ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 443ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 444ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 445ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 446ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v( TAG, "Cannot parse RFC2445 date " + dt2445); 447ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 448ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 449ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 450ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 451ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 452ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 453ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 454ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 455ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 456ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtstart", get2445ToMillis(timezone, dtStart2445)); 457ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtend", get2445ToMillis(timezone, dtEnd2445)); 458ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 459dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff int result = mDb.update("Events", values, "_id=?", 460dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 461ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 462ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 463ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 464ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 465ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 466ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 467ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 468ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void cleanInstancesTable() { 469ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.delete("Instances", null /* where clause */, null /* where args */); 470ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 471ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 472ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 473ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 474ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 475ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 476ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 477ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 478ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 4799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 480ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 481ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if we are in the same time zone 482ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 483ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private boolean isSameTimezone() { 484ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio MetaData.Fields fields = mMetaData.getFields(); 485ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 486ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(fields.timezone, localTimezone); 487ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 488ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 489ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 490ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 491ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 492ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 493ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezoneDatabaseVersion = null; 494ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 495ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 496ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 497ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.e(TAG, "Could not read timezone database version from the cache"); 498ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 499ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 500ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 501ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 502ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 503ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 504ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezoneDatabaseVersion = null; 505ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 506ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 507ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 508ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.e(TAG, "Could not read timezone database version from the cache"); 509ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 510ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 511ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 512ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 513ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 514ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 515ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 5169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 5179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 5189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 5199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 5209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 5219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 5229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 5239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 5249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 5259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 5269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 5279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 5289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 5299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff handleInstanceQuery(qb, begin, end, new String[] { Instances._ID }, 5309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, null /* sort */, false /* searchByDayInsteadOfMillis */); 5319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 5339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void rescheduleMissedAlarms() { 5369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 5379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager != null) { 5389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 5399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = context.getContentResolver(); 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.rescheduleMissedAlarms(cr, context, manager); 5419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Appends comma separated ids. 5469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param ids Should not be empty 5479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void appendIds(StringBuilder sb, HashSet<Long> ids) { 5499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long id : ids) { 5509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.append(id).append(','); 5519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.setLength(sb.length() - 1); // Yank the last comma 5549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void notifyChange() { 5589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 5599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 5609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff getContext().getContentResolver().notifyChange(Calendar.CONTENT_URI, null, 5619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff true /* syncToNetwork */); 5629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 5669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 567ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 568ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 5699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 5729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 5749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 5759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 5769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 5789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 5799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 5809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 5819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 5829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 5841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 5859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 586595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 5879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 5889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 5891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 5909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 591636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 592636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 5939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 59419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 59519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 59619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 59719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 598595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 59919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 60019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 60119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 60219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 603636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 604636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 60519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 60619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 6079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 6089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 609595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 6109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 6129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 613636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 614636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 6159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 6179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 6189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 6199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 6209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 6229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 6249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 6259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 6289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 6309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 6319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return handleInstanceQuery(qb, begin, end, projection, 6339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection, sortOrder, match == INSTANCES_BY_DAY); 6346db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 6359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 6369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 6379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 6399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 6419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 6429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 6459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 6479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 6489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6496db535b458146a279bebd4a51d56c1bdfc204528Erik return handleEventDayQuery(qb, startDay, endDay, projection, selection); 6509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 6511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 6529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 6531ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.appendWhere("Events._id=Attendees.event_id"); 6549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 6561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 6579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 658636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 659636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Attendees._id=? AND Events._id=Attendees.event_id"); 6609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 6629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Reminders"); 6639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 6651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Reminders, Events"); 6669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 667636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 668636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Reminders._id=? AND Events._id=Reminders.event_id"); 6699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 671e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 6729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 673e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 674e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 6759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 677e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 6789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 679e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 680e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 6819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 6829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 684e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 6859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 686636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 687e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 688e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id AND CalendarAlerts._id=?"); 6899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 6919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("ExtendedProperties"); 6929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 6947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff qb.setTables("ExtendedProperties"); 695636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 696636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("ExtendedProperties._id=?"); 6979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 6999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 7009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 7039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 7049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 7079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 7089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 709ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 710ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 711ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 712ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 713ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 714ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 715ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 716ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 717ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 7189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 7199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 7209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 7219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 7229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.setNotificationUri(getContext().getContentResolver(), Calendar.Events.CONTENT_URI); 7239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 7259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 7289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 7299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 7309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 7319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 7329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 7339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 7349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 7359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 7369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 7379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 7389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 7399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 7409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 7419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rangeEnd, String[] projection, 7429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String sort, boolean searchByDay) { 7439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Instances INNER JOIN Events ON (Instances.event_id=Events._id) " + 7459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff "INNER JOIN Calendars ON (Events.calendar_id = Calendars._id)"); 7469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 7479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 7489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 7499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 7509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 7519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 7529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 7539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 7549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 7559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 7569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */); 7578335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("startDay<=? AND endDay>=?"); 7589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 7599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 7609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */); 7618335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("begin<=? AND end>=?"); 7629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7638335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(rangeEnd), 7648335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 7658335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 7667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 7679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7696db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 7706db535b458146a279bebd4a51d56c1bdfc204528Erik String[] projection, String selection) { 7716db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setTables("Instances INNER JOIN Events ON (Instances.event_id=Events._id) " + 7726db535b458146a279bebd4a51d56c1bdfc204528Erik "INNER JOIN Calendars ON (Events.calendar_id = Calendars._id)"); 7736db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 77443556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 77543556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 77643556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff Time time = new Time(); 777192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 77843556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 77943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 780192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 78143556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 78243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff acquireInstanceRange(beginMs, endMs, true); 7838335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("startDay<=? AND endDay>=?"); 7848335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 7858335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 7868335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 7876db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 7889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 7919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 7929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. Acquires the database lock and calls {@link #acquireInstanceRangeLocked}. 7939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 7949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void acquireInstanceRange(final long begin, 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long end, 8009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean useMinimumExpansionWindow) { 8019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow); 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 8139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 8149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 8169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 8179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 8189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void acquireInstanceRangeLocked(long begin, long end, 8199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean useMinimumExpansionWindow) { 8209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 8219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 8229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 8249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 8269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 8289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 8299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 8309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 8319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 8329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 8369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 8379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 8389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 8399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String dbTimezone = fields.timezone; 8409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 8419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 8429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String localTimezone = TimeZone.getDefault().getID(); 8439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean timezoneChanged = (dbTimezone == null) || !dbTimezone.equals(localTimezone); 8449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (maxInstance == 0 || timezoneChanged) { 8469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 8479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.execSQL("DELETE FROM Instances;"); 8489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Config.LOGV) { 8496db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 8509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 8519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandInstanceRangeLocked(expandBegin, expandEnd, localTimezone); 8539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 854dbd797d59294d72d7ea9226d10128674b634aaadErik mMetaData.writeLocked(localTimezone, expandBegin, expandEnd); 8559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 8569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 8599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 8609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 8619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 8629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 8639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 8659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 8669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 8699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Config.LOGV) { 8719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandInstanceRangeLocked(expandBegin, minInstance, localTimezone); 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 8829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 8859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 8869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 8879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandInstanceRangeLocked(maxInstance, expandEnd, localTimezone); 8889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 8899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 892dbd797d59294d72d7ea9226d10128674b634aaadErik mMetaData.writeLocked(localTimezone, minInstance, maxInstance); 8939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EXPAND_COLUMNS = new String[] { 8969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._ID, 8979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 8989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.STATUS, 8999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTSTART, 9009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTEND, 9019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EVENT_TIMEZONE, 9029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 9039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 9049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXRULE, 9059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXDATE, 9069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DURATION, 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ALL_DAY, 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 9099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_INSTANCE_TIME 9109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 9119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make instances for the given range. 9149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void expandInstanceRangeLocked(long begin, long end, String localTimezone) { 9169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 9189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.startMethodTracing("expandInstanceRangeLocked"); 9199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Expanding events between " + begin + " and " + end); 9239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getEntries(begin, end); 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff performInstanceExpansion(begin, end, localTimezone, entries); 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 9349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.stopMethodTracing(); 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Get all entries affecting the given window. 9409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 9419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 9429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return Cursor for the entries; caller must close it. 9439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getEntries(long begin, long end) { 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 9461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 9479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 9489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String beginString = String.valueOf(begin); 9509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String endString = String.valueOf(end); 9519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // grab recurrence exceptions that fall outside our expansion window but modify 9539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences that do fall within our window. we won't insert these into the output 9549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set of instances, but instead will just add them to our cancellations list, so we 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // can cancel the correct recurrence expansion instances. 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we don't have originalInstanceDuration or end time. for now, assume the original 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance lasts no longer than 1 week. 9582d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio // also filter with syncable state (we dont want the entries from a non syncable account) 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: compute the originalInstanceEndTime or get this from the server. 9602d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio qb.appendWhere("((dtstart <= ? AND (lastDate IS NULL OR lastDate >= ?)) OR " + 9618335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff "(originalInstanceTime IS NOT NULL AND originalInstanceTime <= ? AND " + 9622d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio "originalInstanceTime >= ?)) AND (sync_events != 0)"); 9638335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {endString, beginString, endString, 9648335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(begin - MAX_ASSUMED_DURATION)}; 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 9669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Retrieving events to expand: " + qb.toString()); 9679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return qb.query(mDb, EXPAND_COLUMNS, null /* selection */, 9708335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff selectionArgs, null /* groupBy */, 9717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 9729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Perform instance expansion on the given entries. 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 9779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param localTimezone 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param entries The entries to process. 9809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void performInstanceExpansion(long begin, long end, String localTimezone, 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries) { 9839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 9849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int statusColumn = entries.getColumnIndex(Events.STATUS); 9869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtstartColumn = entries.getColumnIndex(Events.DTSTART); 9879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtendColumn = entries.getColumnIndex(Events.DTEND); 9889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int eventTimezoneColumn = entries.getColumnIndex(Events.EVENT_TIMEZONE); 9899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int durationColumn = entries.getColumnIndex(Events.DURATION); 9909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rruleColumn = entries.getColumnIndex(Events.RRULE); 9919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rdateColumn = entries.getColumnIndex(Events.RDATE); 9929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exruleColumn = entries.getColumnIndex(Events.EXRULE); 9939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exdateColumn = entries.getColumnIndex(Events.EXDATE); 9949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int allDayColumn = entries.getColumnIndex(Events.ALL_DAY); 9959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int idColumn = entries.getColumnIndex(Events._ID); 9969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int syncIdColumn = entries.getColumnIndex(Events._SYNC_ID); 9979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalEventColumn = entries.getColumnIndex(Events.ORIGINAL_EVENT); 9989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalInstanceTimeColumn = entries.getColumnIndex(Events.ORIGINAL_INSTANCE_TIME); 9999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues initialValues; 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff EventInstancesMap instancesMap = new EventInstancesMap(); 10029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 10049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time eventTime = new Time(); 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: entries contains all events that affect the current 10079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window. It consists of: 10089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. These will be 10099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed. 10109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Recurrences that included the window. These will be displayed 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if not canceled. 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Recurrence exceptions that fall in the window. These will be 10139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed if not cancellations. 10149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 10159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window (subject to 1 week assumption above), but are outside 10169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the window. These will not be displayed. Cases c and d are 10179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // distingushed by the start / end time. 10189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (entries.moveToNext()) { 10209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 10219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = null; 10229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = entries.getInt(allDayColumn) != 0; 10249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String eventTimezone = entries.getString(eventTimezoneColumn); 10269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(eventTimezone)) { 10279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the events table, allDay events start at midnight. 10289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this forces them to stay at midnight for all day events 10299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: check that this actually does the right thing. 10309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTimezone = Time.TIMEZONE_UTC; 10319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = entries.getLong(dtstartColumn); 10349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long eventId = Long.valueOf(entries.getLong(idColumn)); 10359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = entries.getString(durationColumn); 10379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 10409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff catch (DateException e) { 10429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "error parsing duration for event " 10439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + eventId + "'" + durationStr + "'", e); 10449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 10459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 10469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 10479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 10489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 10499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 10509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 10519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String syncId = entries.getString(syncIdColumn); 10559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = entries.getString(originalEventColumn); 10569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalInstanceTimeMillis = -1; 10589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(originalInstanceTimeColumn)) { 10599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis= entries.getLong(originalInstanceTimeColumn); 10609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = entries.getInt(statusColumn); 10629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rruleStr = entries.getString(rruleColumn); 10649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rdateStr = entries.getString(rdateColumn); 10659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exruleStr = entries.getString(exruleColumn); 10669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exdateStr = entries.getString(exdateColumn); 10679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1068f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 1069f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 1070f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(rruleStr, rdateStr, exruleStr, exdateStr); 1071f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 1072f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + rruleStr, e); 1073f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio continue; 1074f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 10759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1076f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 10779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating 10789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (status == Events.STATUS_CANCELED) { 10809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen! 10819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "Found canceled recurring event in " 10829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "Events table. Ignoring."); 10839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 10849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // need to parse the event into a local calendar. 10879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = eventTimezone; 10889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.set(dtstartMillis); 10899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.allDay = allDay; 10909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 10929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen. 10939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "Repeating event has no duration -- " 10949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "should not happen."); 10959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 10969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set to one day. 10979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 10989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 10999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 1; 11009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 11019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 11029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 11039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P1D"; 11049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // compute the duration from dtend, if we can. 11069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // otherwise, use 0s. 11079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 11089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 11099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 11109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 11119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 11129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 11139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = entries.getLong(dtendColumn); 11149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = (int) ((dtendMillis - dtstartMillis) / 1000); 11159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P" + duration.seconds + "S"; 11169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 11189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 11199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long[] dates; 11249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dates = rp.expand(eventTime, recur, begin, end); 11259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Initialize the "eventTime" timezone outside the loop. 11279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is used in computeTimezoneDependentFields(). 11289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 11299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 11309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 11329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long durationMillis = duration.getMillis(); 11359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long date : dates) { 11369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 11379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 11389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.BEGIN, date); 11409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = date + durationMillis; 11419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 11429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(date, dtendMillis, 11449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 11459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instancesMap.add(syncId, initialValues); 11469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating 11499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 11509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if this event has an "original" field, then record 11529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that we need to cancel the original event (we can't 11539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do that here because the order of this loop isn't 11549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // defined) 11559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 11569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.ORIGINAL_EVENT, originalEvent); 11579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.ORIGINAL_INSTANCE_TIME, 11589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis); 11599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, status); 11609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = dtstartMillis; 11639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 11649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 11659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = entries.getLong(dtendColumn); 11669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = duration.addTo(dtstartMillis); 11699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this non-recurring event might be a recurrence exception that doesn't 11729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // actually fall within our expansion window, but instead was selected 11739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so we can correctly cancel expanded recurrence instances below. do not 11749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // add events to the instances map if they don't actually fall within our 11759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansion window. 11769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((dtendMillis < begin) || (dtstartMillis > end)) { 11779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 11789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, Events.STATUS_CANCELED); 11799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "Unexpected event outside window: " + syncId); 11819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 11829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 11869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.BEGIN, dtstartMillis); 11879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 11899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 11919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 11929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 11949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, 11969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 11979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instancesMap.add(syncId, initialValues); 11999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 12019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "RecurrenceProcessor error ", e); 12029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (TimeFormatException e) { 12039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "RecurrenceProcessor error ", e); 12049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains all instances that affect the 12089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window, indexed by original sync id. It consists of: 12099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. They have: 12109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 12119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window. They may 12129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be subject to exceptions. They have: 12139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 12149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They have: 12159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // ORIGINAL_EVENT, ORIGINAL_INSTANCE_TIME, STATUS (since they can 12169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be a modification or cancellation), EVENT_ID, BEGIN, END 12179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 12189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. They have: 12199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // ORIGINAL_EVENT, ORIGINAL_INSTANCE_TIME, STATUS = 12209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED, EVENT_ID, BEGIN, END 12219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // First, delete the original instances corresponding to recurrence 12239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exceptions. We do this by iterating over the list and for each 12249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrence exception, we search the list for an instance with a 12259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // matching "original instance time". If we find such an instance, 12269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we remove it from the list. If we don't find such an instance 12279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then we cancel the recurrence exception. 12289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Set<String> keys = instancesMap.keySet(); 12299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (String syncId : keys) { 12309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff InstancesList list = instancesMap.get(syncId); 12319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 12329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If this instance is not a recurrence exception, then 12349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // skip it. 12359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.ORIGINAL_EVENT)) { 12369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 12379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 12409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 12419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff InstancesList originalList = instancesMap.get(originalEvent); 12429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalList == null) { 12439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The original recurrence is not present, so don't try canceling it. 12449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 12459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Search the original event for a matching original 12489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance time. If there is a matching one, then remove 12499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original one. We do this both for exceptions that 12509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // change the original instance as well as for exceptions 12519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that delete the original instance. 12529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (int num = originalList.size() - 1; num >= 0; num--) { 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues originalValues = originalList.get(num); 12549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginTime = originalValues.getAsLong(Instances.BEGIN); 12559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (beginTime == originalTime) { 12569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We found the original instance, so remove it. 12579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalList.remove(num); 12589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains filtered instances. 12649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // It consists of: 12659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. 12669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window and have not 12679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // been subject to exceptions. 12689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They will have 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED if they are cancellations. 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 12719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. These are STATUS_CANCELED. 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do the inserts. Since the db lock is held when this method is executed, 12749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this will be done in a transaction. 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if there is lock contention (e.g., a sync is trying to merge into the db 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // while the calendar app is trying to query the db (expanding instances)), we will 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // not be "polite" and yield the lock until we're done. This will favor local query 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // operations over sync/write operations. 12799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (String syncId : keys) { 12809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff InstancesList list = instancesMap.get(syncId); 12819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 12829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If this instance was cancelled then don't create a new 12849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance. 12859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer status = values.getAsInteger(Events.STATUS); 12869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (status != null && status == Events.STATUS_CANCELED) { 12879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 12889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Remove these fields before inserting a new instance 12919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.ORIGINAL_EVENT); 12929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 12939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.STATUS); 12949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1295c874ed5c6cc0fcc6ac06ae7d20db0eab7d749608Ken Shirriff mDbHelper.instancesReplace(values); 12969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Computes the timezone-dependent fields of an instance of an event and 13029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * updates the "values" map to contain those fields. 13039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin the start time of the instance (in UTC milliseconds) 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end the end time of the instance (in UTC milliseconds) 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param local a Time object with the timezone set to the local timezone 13079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values a map that will contain the timezone-dependent fields 13089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 13099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void computeTimezoneDependentFields(long begin, long end, 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local, ContentValues values) { 13119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(begin); 13129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay = Time.getJulianDay(begin, local.gmtoff); 13139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startMinute = local.hour * 60 + local.minute; 13149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(end); 13169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay = Time.getJulianDay(end, local.gmtoff); 13179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endMinute = local.hour * 60 + local.minute; 13189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Special case for midnight, which has endMinute == 0. Change 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that to +24 hours on the previous day to make everything simpler. 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Exception: if start and end minute are both 0 on the same day, 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then leave endMinute alone. 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (endMinute == 0 && endDay > startDay) { 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endMinute = 24 * 60; 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay -= 1; 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_DAY, startDay); 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_DAY, endDay); 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_MINUTE, startMinute); 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_MINUTE, endMinute); 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 13399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 13409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 13529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 13546db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 135648587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 135748587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 13589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 13599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static boolean isRecurrenceEvent(ContentValues values) { 13649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return (!TextUtils.isEmpty(values.getAsString(Events.RRULE))|| 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.RDATE))|| 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT))); 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 13709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected Uri insertInTransaction(Uri uri, ContentValues values) { 1371ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 13739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 13769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 13799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 13809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 13867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 13877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 13887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 13919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: avoid the call to updateBundleFromEvent if this is just finding local 13937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // changes. 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues updatedValues = updateContentValuesFromEvent(values); 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 13979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 13989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 13999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 14019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 14029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 14039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 14049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 14059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 14069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 14079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 14089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 14099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, true /* new event */, mDb); 14179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 14229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 14269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff triggerAppWidgetUpdate(id); 14289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 14319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 14329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountName = values.getAsString(Calendars._SYNC_ACCOUNT); 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars._SYNC_ACCOUNT_TYPE); 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = values.getAsString(Calendars.URL); 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, false /* two-way sync */, calendarUrl); 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 14459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.attendeesInsert(values); 14487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 14497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Attendees.EVENT_ID)); 14507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 14519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 14549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 14569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.remindersInsert(values); 14617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 14627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Reminders.EVENT_ID)); 14637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 14649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 14689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 14739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 14759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 14772fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 14782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 14819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Calendar.ExtendedProperties.EVENT_ID)) { 14829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 14849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.extendedPropertiesInsert(values); 14867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 14877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Calendar.ExtendedProperties.EVENT_ID)); 14887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 14899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 14909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 14919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 14929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 14949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 14959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 14969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 14976db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 14987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 14999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 15009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 15019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 15049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 15059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 15089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private void setEventDirty(int eventId) { 1511636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.execSQL("UPDATE Events SET _sync_dirty=1 where _id=?", new Integer[] {eventId}); 15127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 15137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 15149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 15159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 15169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 15179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 15189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 15199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 1520f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 1521f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 1522f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 1523f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 15249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 15259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 15269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 15279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 15289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 15299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 15309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 15319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 15329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 15339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 15349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 15359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 15369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 15389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 15399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 15409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 15419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 15449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 15479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 15489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 15499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 15509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 15519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 15529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 15539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 15549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 15559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 15569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 15579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 15589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 15599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 15609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 15619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 15629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 15639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 15649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 15659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 15679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 15689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 15699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 15729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 15739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 15749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 15759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 15769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 15779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 15789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 15799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 15809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 15819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 15829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 15849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 15859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 15869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 15879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 15889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 15899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 15909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 15919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 15929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 15939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 15949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 15959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 15969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 15989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 15999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 16009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 16019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 16059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 16069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 16079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 16089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 16099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 16109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 16119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 16129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 16139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 16149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 16159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 16169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 16189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 16199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 16209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 16219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 16259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 16269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 16299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 16309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 16319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 16329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 16359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 16369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 16379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 16389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 16399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 16439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 16449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 16459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 16469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 16479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 16519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 16529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 16559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 1656636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.update("Events", values, "_id=?", new String[] {String.valueOf(eventId)}); 16579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 16609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the instances table when an event is added or updated. 16619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 16629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 16639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param newEvent true if the event is new. 16649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 16659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 16669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateInstancesLocked(ContentValues values, 16679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 16689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean newEvent, 16699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 16709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If there are no expanded Instances, then return. 16729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 16739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (fields.maxInstance == 0) { 16749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 16759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 16789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis == null) { 16799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (newEvent) { 16809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // must be present for a new event. 16819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART missing."); 16829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Config.LOGV) Log.v(TAG, "Missing DTSTART. " 16849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "No need to update instance."); 16859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 16869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 16899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 16909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!newEvent) { 16929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Want to do this for regular event, recurrence, or exception. 16939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // For recurrence or exception, more deletion may happen below if we 16949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do an instance expansion. This deletion will suffice if the exception 16959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // is moved outside the window, for instance. 1696636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.delete("Instances", "event_id=?", new String[] {String.valueOf(rowId)}); 16979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (isRecurrenceEvent(values)) { 17009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The recurrence or exception needs to be (re-)expanded if: 17019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Exception or recurrence that falls inside window 17029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean insideWindow = dtstartMillis <= fields.maxInstance && 17039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff (lastDateMillis == null || lastDateMillis >= fields.minInstance); 17049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Exception that affects instance inside window 17059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // These conditions match the query in getEntries 17069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // See getEntries comment for explanation of subtracting 1 week. 17079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean affectsWindow = originalInstanceTime != null && 17089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime <= fields.maxInstance && 17099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime >= fields.minInstance - MAX_ASSUMED_DURATION; 17109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (insideWindow || affectsWindow) { 17119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateRecurrenceInstancesLocked(values, rowId, db); 17129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: an exception creation or update could be optimized by 17149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // updating just the affected instances, instead of regenerating 17159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the recurrence. 17169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 17179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 17209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis == null) { 17219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = dtstartMillis; 17229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if the event is in the expanded range, insert 17259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // into the instances table. 17269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: deal with durations. currently, durations are only used in 17279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences. 17289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis <= fields.maxInstance && dtendMillis >= fields.minInstance) { 17309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues instanceValues = new ContentValues(); 17319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.EVENT_ID, rowId); 17329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.BEGIN, dtstartMillis); 17339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.END, dtendMillis); 17349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 17369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 17379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 17389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 17399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the timezone-dependent fields. 17429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local = new Time(); 17439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 17449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = Time.TIMEZONE_UTC; 17459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 17469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = fields.timezone; 17479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, local, instanceValues); 17509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.instancesInsert(instanceValues); 17519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 17559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Determines the recurrence entries associated with a particular recurrence. 17569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This set is the base recurrence and any exception. 17579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 17589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Normally the entries are indicated by the sync id of the base recurrence 17599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * (which is the originalEvent in the exceptions). 17609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * However, a complication is that a recurrence may not yet have a sync id. 17619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * In that case, the recurrence is specified by the rowId. 17629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 17639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param recurrenceSyncId The sync id of the base recurrence, or null. 17649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The row id of the base recurrence. 17659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return the relevant entries. 17669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 17679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getRelevantRecurrenceEntries(String recurrenceSyncId, long rowId) { 17689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 17699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 17719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 1772636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String selectionArgs[]; 17739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 1774636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_id =?"; 17759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 1776636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {String.valueOf(rowId)}; 17779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 1778636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_sync_id = ? OR originalEvent = ?"; 17799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 1780636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {recurrenceSyncId, recurrenceSyncId}; 17819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 17839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Retrieving events to expand: " + qb.toString()); 17849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1786636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return qb.query(mDb, EXPAND_COLUMNS, null /* selection */, selectionArgs, 17877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* groupBy */, null /* having */, null /* sortOrder */); 17889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 17919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Do incremental Instances update of a recurrence or recurrence exception. 17929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 17939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method does performInstanceExpansion on just the modified recurrence, 17949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid the overhead of recomputing the entire instance table. 17959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 17969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 17979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 17989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 17999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 18009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateRecurrenceInstancesLocked(ContentValues values, 18019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 18029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 18039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 18049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 18059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String recurrenceSyncId = null; 18069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null) { 18079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = originalEvent; 18089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 18099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the recurrence's sync id from the database 18109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = DatabaseUtils.stringForQuery(db, "SELECT _sync_id FROM Events" 1811636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff + " WHERE _id=?", new String[] {String.valueOf(rowId)}); 18129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrenceSyncId is the _sync_id of the underlying recurrence 18149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the recurrence hasn't gone to the server, it will be null. 18159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to clear out old instances 18179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 18189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating updating a recurrence that hasn't gone to the server. 18199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to delete based on row id 18209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 18219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 18229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 18239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._id =?)"; 18249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{"" + rowId}); 18259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 18269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating or modifying a recurrence or exception. 18279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Delete instances for recurrence (_sync_id = recurrenceSyncId) 18289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // and all exceptions (originalEvent = recurrenceSyncId) 18299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 18309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 18319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 18329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._sync_id =?" 18339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " OR Events.originalEvent =?)"; 18349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{recurrenceSyncId, recurrenceSyncId}); 18359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do instance expansion 18389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getRelevantRecurrenceEntries(recurrenceSyncId, rowId); 18399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 18409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff performInstanceExpansion(fields.minInstance, fields.maxInstance, fields.timezone, 18419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries); 18429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 18439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 18449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 18459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1848dbd797d59294d72d7ea9226d10128674b634aaadErik // Clear busy bits (is this still needed?) 1849dbd797d59294d72d7ea9226d10128674b634aaadErik mMetaData.writeLocked(fields.timezone, fields.minInstance, fields.maxInstance); 18509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 18539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 18549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 18559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 18569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 18579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 18589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 18599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 18609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 18619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 18629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 18639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 18649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 18669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 18689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 18699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 18719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 18729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 18739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 18749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 18759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 18769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 18779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 18789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 18799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 18809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 18819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 18829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 18839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 18849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1886f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 1887f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 1888f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 1889f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 1890f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 1891f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio values.get(Calendar.Events.RRULE), e); 1892f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 1893f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 18949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1895f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 18969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 18979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 18989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 19009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 19029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 19039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 19049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 19069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 19089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 19109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 19119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 19129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 19139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 19159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 19169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 19179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 19209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 19219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 19239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private ContentValues updateContentValuesFromEvent(ContentValues initialValues) { 19269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 19279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(initialValues); 19289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 19309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 19319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 19329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 19359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 19369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 19379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "Could not calculate last date.", e); 19389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 19399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 19439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 19449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("event_id", eventId); 19469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 19489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 19509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 19519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 19529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 19539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 19569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 19579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 19589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 19619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 19629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 19639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 19649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 19659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtstart2445", time.format2445()); 19669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 19699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 19709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 19719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtend2445", time.format2445()); 19729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 19759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 19769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 19779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 19789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 19799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 19809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 19819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 19829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 19849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("originalInstanceTime2445", time.format2445()); 19859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 19889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 19899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 19909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 19919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("lastDate2445", time.format2445()); 19929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 19959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 19989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) { 1999ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 20009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 20019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 20039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 20049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 20059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 20069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 20079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 20089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 2010dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 20119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 20129323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2013dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2014dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2015dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2016dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 20179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20181ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 20199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 20207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 20211ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection = appendAccountToSelection(uri, selection); 20227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 20231ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 20241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", ID_ONLY_PROJECTION, 20251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 20267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 20279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 20281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 20291ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 20301ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result += deleteEventInternal(id, callerIsSyncAdapter); 20319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 20339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 20349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 20359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 20379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 20391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 20401ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 20411ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 20421ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 20431ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 20441ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 20451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 20461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return deleteEventInternal(id, callerIsSyncAdapter); 20471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 20489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 20499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 20507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 20517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Attendees", selection, selectionArgs); 20527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 20537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Attendees", uri, selection, selectionArgs); 20547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 20579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 20582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 20592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 20602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 20627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2063636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Attendees", "_id=?", new String[] {String.valueOf(id)}); 20647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 20652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Attendees", uri, null /* selection */, 20662fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 20677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 20717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 20727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Reminders", selection, selectionArgs); 20737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 20747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Reminders", uri, selection, selectionArgs); 20757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 20789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 20792fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 20802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 20812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20827e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 20837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2084636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Reminders", "_id=?", new String[] {String.valueOf(id)}); 20857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 20862fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Reminders", uri, null /* selection */, 20872fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 20882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 20912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 20922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 20932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return mDb.delete("ExtendedProperties", selection, selectionArgs); 20942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 20952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, selection, selectionArgs); 20962fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20972fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 20982fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 20992fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 21002fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 21012fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 21022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 21032fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 21042fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2105636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("ExtendedProperties", "_id=?", 2106636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 21072fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 21082fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, null /* selection */, 21092fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 21107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 21147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 21157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("CalendarAlerts", selection, selectionArgs); 21167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 21177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("CalendarAlerts", uri, selection, selectionArgs); 21187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 21222fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 21232fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 21242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 21252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 21262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 21279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2128636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("CalendarAlerts", "_id=?", new String[] {String.valueOf(id)}); 21299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 21317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot delete that URL: " + uri); 21329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 21339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder("_id="); 21349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 21359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 21369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 21379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 21389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2143595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 21447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteMatchingCalendars(selection); // TODO: handle in sync adapter 21459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 21469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 21476db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 21489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 21499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 21509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 21519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private int deleteEventInternal(long id, boolean callerIsSyncAdapter) { 21551ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2156192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 21571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 21581ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 21591ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", EVENTS_PROJECTION, 2160192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank "_id=?", selectionArgs, 2161636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 21621ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 21631ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 21641ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 21651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 21661ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 21671ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (!TextUtils.isEmpty(syncId)) { 21681ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 21691ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // TODO: we may also want to delete exception 21701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // events for this event (in case this was a 21711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // recurring event). We can do that with the 21721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // following code: 21731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // mDb.delete("Events", "originalEvent=?", new String[] {syncId}); 21741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 21751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 21761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 21771ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 21781ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 21791ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 21801ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 21811ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String origEvent = cursor.getString(EVENTS_ORIGINAL_EVENT_INDEX); 21821ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate) 21831ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff || !TextUtils.isEmpty(origEvent)) { 21841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 21851ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 21861ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 21871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (callerIsSyncAdapter) { 2188192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.delete("Events", "_id=?", selectionArgs); 2189192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.delete("Attendees", "event_id=?", selectionArgs); 21901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 21911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 21921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events.DELETED, 1); 21931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 2194192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.update("Events", values, "_id=?", selectionArgs); 21951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 21961ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 21971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 21981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 21991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 22001ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 22018f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 22028f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff scheduleNextAlarm(false /* do not remove alarms */); 22031ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff triggerAppWidgetUpdate(-1); 22041ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 2205192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank // Delete associated data; attendees, however, are deleted with the actual event so 2206192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank // that the sync adapter is able to notify attendees of the cancellation. 2207636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("Instances", "event_id=?", selectionArgs); 2208636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("EventsRawTimes", "event_id=?", selectionArgs); 2209636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("Reminders", "event_id=?", selectionArgs); 2210636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("CalendarAlerts", "event_id=?", selectionArgs); 2211636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("ExtendedProperties", "event_id=?", selectionArgs); 22121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 22131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 22141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 22157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 22167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 22177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 22187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 22197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 22207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 22217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 22227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 22237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 22247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 22257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 22267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues values = new ContentValues(); 22277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, "1"); 22287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 22297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 22307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 22317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 22327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2233636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete(table, "_id=?", new String[] {String.valueOf(id)}); 2234636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", values, "_id=?", new String[] {String.valueOf(event_id)}); 22357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 22367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 22387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 22397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 22417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 22437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 22447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 22457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 22467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 22477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 22487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 22497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 22507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 22517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 22527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 22537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 22547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 22557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 22567e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues dirtyValues = new ContentValues(); 22577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff dirtyValues.put(Events._SYNC_DIRTY, "1"); 22587e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 22597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 22607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 22617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 22627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2263636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update(table, values, "_id=?", new String[] {String.valueOf(id)}); 2264636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", dirtyValues, "_id=?", new String[] {String.valueOf(event_id)}); 22657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 22667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 22687e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 22697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 22717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private int deleteMatchingCalendars(String where) { 22749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 22759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 22769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 22779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = mDb.query("Calendars", sCalendarsIdProjection, where, 22797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* selectionArgs */, null /* groupBy */, 22807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 22819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 22829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 22839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 22869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 22879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 22889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22922cc859cab85391a240b9c3f28c935d919c8ceb8cKen Shirriff return mDb.delete("Calendars", where, null /* whereArgs */); 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: call calculateLastDate()! 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] selectionArgs) { 2299ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 23099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 23117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS && match != EVENTS) { 23129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException( 23139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff "WHERE based updates not supported"); 23149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 23169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 23189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 23199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 23219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 2322dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 2323dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 23249323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2325dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2326dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2327dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 23289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 23322fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 23332fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 23342fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2341636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Calendars", values, "_id=?", 2342636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 23439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 23479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 23489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 23497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = 0; 23507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (match == EVENTS_ID) { 23517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = ContentUris.parseId(uri); 2352a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else if (callerIsSyncAdapter) { 2353a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff if (selection != null && selection.startsWith("_id=")) { 23547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // The ContentProviderOperation generates an _id=n string instead of 23557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // adding the id to the URL, so parse that out here. 23567e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = Long.parseLong(selection.substring(4)); 2357a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else { 2358a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // Sync adapter Events operation affects just Events table, not associated 2359a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // tables. 2360a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff return mDb.update("Events", values, selection, selectionArgs); 2361a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } 23627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2363a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 23647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 23667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 23677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Disallow updating the attendee status in the Events 23699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // table. In the future, we could support this but we 23709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // would have to query and update the attendees table 23719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to keep the values consistent. 23729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 23739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 23749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.SELF_ATTENDEE_STATUS 23759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 23769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // TODO: should we allow this? 23797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (values.containsKey(Events.HTML_URI) && !callerIsSyncAdapter) { 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 23819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.HTML_URI 23829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 23839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues updatedValues = updateContentValuesFromEvent(values); 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "Could not update event."); 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2391636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Events", updatedValues, "_id=?", 2392636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 23939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (result > 0) { 23949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 23959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, false /* not a new event */, mDb); 23969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTSTART)) { 23989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The start time of the event changed, so run the 23999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarm scheduler. 24009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 24019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing event"); 24029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 24049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff triggerAppWidgetUpdate(id); 24059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 24089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24092fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 24102fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 24112fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 24122fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 24139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 24149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 24177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2418636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("Attendees", values, "_id=?", 241983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 24207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 24212fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("Attendees", values, uri, null /* selection */, 24222fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 24237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 24249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 24262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 24272fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 24282fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 24292fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 24302fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 24319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2432636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("CalendarAlerts", values, "_id=?", 2433636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 24349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24352fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 24362fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 24372fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 24389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDb.update("CalendarAlerts", values, selection, selectionArgs); 24399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24402fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 24412fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 24422fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 24432fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 24447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 24457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2446636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff count = mDb.update("Reminders", values, "_id=?", 244783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 24487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 24492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff count = updateInTable("Reminders", values, uri, null /* selection */, 24502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 24517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 24527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 24549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 24559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 24569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 24579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 24597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 24609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24612fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 24622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 24632fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 24642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 24657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 24667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2467636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("ExtendedProperties", values, "_id=?", 2468636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 24697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 24702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("ExtendedProperties", values, uri, null /* selection */, 24712fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 24727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 24739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 247483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 247583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 247683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 247783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(false); 247883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 247983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 248083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 248183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(true); 248283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 248383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 24869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 24879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2490595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) { 2491595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountName = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_NAME); 2492595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountType = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_TYPE); 2493595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 2494595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere(Calendar.Calendars._SYNC_ACCOUNT + "=" 2495595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 2496595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 2497595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 2498595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 2499595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere("1"); // I.e. always true 2500595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 2501595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 2502595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 25039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 2504595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountName = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_NAME); 2505595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountType = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_TYPE); 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 25079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder(Calendar.Calendars._SYNC_ACCOUNT + "=" 25089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 25099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 25109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 25119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 25129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 25139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 25149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 25159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selectionSb.toString(); 25179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 25189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 25199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 25239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 25249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 25259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 25269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] {Calendars._SYNC_ACCOUNT, Calendars._SYNC_ACCOUNT_TYPE, 25279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars.URL, Calendars.SYNC_EVENTS}, 25289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 25299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 25309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 25319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 25339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 25349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 25359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null && cursor.moveToFirst()) { 25369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 25379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final String accountName = cursor.getString(0); 25389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final String accountType = cursor.getString(1); 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff account = new Account(accountName, accountType); 25409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarUrl = cursor.getString(2); 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 25429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 25439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 25449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25479535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 25489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.w(TAG, "Cannot update subscription because account " 25509535627bf6295cd94447beb83e1aac41f50c3600Erik + "is empty -- should not happen."); 25519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25549535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 25559535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 25569535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 25579535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 25589535627bf6295cd94447beb83e1aac41f50c3600Erik } 25599535627bf6295cd94447beb83e1aac41f50c3600Erik 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 25669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 25679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 25689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this needed 25719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// @Override 25729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// public void onSyncStop(SyncContext context, boolean success) { 25739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// super.onSyncStop(context, success); 25749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// if (Log.isLoggable(TAG, Log.DEBUG)) { 25759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// Log.d(TAG, "onSyncStop() success: " + success); 25769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 25779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// scheduleNextAlarm(false /* do not remove alarms */); 25789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// triggerAppWidgetUpdate(-1); 25799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 25809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 25829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Update any existing widgets with the changed events. 25839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 25849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param changedEventId Specific event known to be changed, otherwise -1. 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * If present, we use it to decide if an update is necessary. 25869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 25879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private synchronized void triggerAppWidgetUpdate(long changedEventId) { 25889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 25899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (context != null) { 25909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mAppWidgetProvider.providerUpdated(context, changedEventId); 25919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Retrieve and cache the alarm manager */ 25959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager getAlarmManager() { 25969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff synchronized(mAlarmLock) { 25979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (mAlarmManager == null) { 25989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 25999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (context == null) { 26009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "getAlarmManager() cannot get Context"); 26019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 26029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Object service = context.getSystemService(Context.ALARM_SERVICE); 26049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mAlarmManager = (AlarmManager) service; 26059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mAlarmManager; 26079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarmCheck(long triggerTime) { 26119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 26129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager == null) { 26139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "scheduleNextAlarmCheck() cannot get AlarmManager"); 26149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 26159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 26179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Intent intent = new Intent(CalendarReceiver.SCHEDULE); 26189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff intent.setClass(context, CalendarReceiver.class); 26199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff PendingIntent pending = PendingIntent.getBroadcast(context, 26209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_NO_CREATE); 26219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (pending != null) { 26229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Cancel any previous alarms that do the same thing. 26239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.cancel(pending); 26249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff pending = PendingIntent.getBroadcast(context, 26269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 26279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 26299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 26309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(triggerTime); 26319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 26329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "scheduleNextAlarmCheck at: " + triggerTime + timeStr); 26339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.set(AlarmManager.RTC_WAKEUP, triggerTime, pending); 26369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs the alarm scheduler in a background thread. 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarm(boolean removeAlarms) { 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new AlarmScheduler(removeAlarms); 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread and schedules an alarm for 26489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the next calendar event, if necessary. 26499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void runScheduleNextAlarm(boolean removeAlarms) { 26519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 26529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.beginTransaction(); 26539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (removeAlarms) { 26559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff removeScheduledAlarmsLocked(db); 26569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarmLocked(db); 26589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.setTransactionSuccessful(); 26599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 26609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.endTransaction(); 26619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method looks at the 24-hour window from now for any events that it 26669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * needs to schedule. This method runs within a database transaction. It 26679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * also runs in a background thread. 26689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The CalendarProvider2 keeps track of which alarms it has already scheduled 26709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid scheduling them more than once and for debugging problems with 26719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms. It stores this knowledge in a database table called CalendarAlerts 26729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * which persists across reboots. But the actual alarm list is in memory 26739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and disappears if the phone loses power. To avoid missing an alarm, we 26749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * clear the entries in the CalendarAlerts table when we start up the 26759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * CalendarProvider2. 26769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Scheduling an alarm multiple times is not tragic -- we filter out the 26789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * extra ones when we receive them. But we still need to keep track of the 26799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled alarms. The main reason is that we need to prevent multiple 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * notifications for the same alarm (on the receive side) in case we 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * accidentally schedule the same alarm multiple times. We don't have 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * visibility into the system's alarm list so we can never know for sure if 26839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we have already scheduled an alarm and it's better to err on scheduling 26849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * an alarm twice rather than missing an alarm. Another reason we keep 26859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * track of scheduled alarms in a database table is that it makes it easy to 26869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * run an SQL query to find the next reminder that we haven't scheduled. 26879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 26899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void scheduleNextAlarmLocked(SQLiteDatabase db) { 26919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager alarmManager = getAlarmManager(); 26929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmManager == null) { 26939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "Failed to find the AlarmManager. Could not schedule the next alarm!"); 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long currentMillis = System.currentTimeMillis(); 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long start = currentMillis - SCHEDULE_ALARM_SLACK; 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long end = start + (24 * 60 * 60 * 1000); 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = getContext().getContentResolver(); 27019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 27039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(start); 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "runScheduleNextAlarm() start search: " + startTimeStr); 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27088f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Delete rows in CalendarAlert where the corresponding Instance or 27098f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Reminder no longer exist. 27108f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Also clear old alarms but keep alarms around for a while to prevent 27119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // multiple alerts for the same reminder. The "clearUpToTime' 27129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should be further in the past than the point in time where 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we start searching for events (the "start" variable defined above). 27148f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff String selectArg[] = new String[] { 27158f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Long.toString(currentMillis - CLEAR_OLD_ALARM_THRESHOLD) 27168f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff }; 27178f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 27188f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff int rowsDeleted = 27198f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff db.delete(CalendarAlerts.TABLE_NAME, INVALID_CALENDARALERTS_SELECTOR, selectArg); 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long nextAlarmTime = end; 27228f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long tmpAlarmTime = CalendarAlerts.findNextAlarmTime(cr, currentMillis); 27238f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (tmpAlarmTime != -1 && tmpAlarmTime < nextAlarmTime) { 27248f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff nextAlarmTime = tmpAlarmTime; 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Extract events from the database sorted by alarm time. The 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // alarm times are computed from Instances.begin (whose units 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // are milliseconds) and Reminders.minutes (whose units are 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // minutes). 27319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events whose end time is already in the past. 27339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events alarms that we have already scheduled. 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 1: we can add support for the case where Reminders.minutes 27369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // equals -1 to mean use Calendars.minutes by adding a UNION for 27379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that case where the two halves restrict the WHERE clause on 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders.minutes != -1 and Reminders.minutes = 1, respectively. 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 2: we have to name "myAlarmTime" different from the 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "alarmTime" column in CalendarAlerts because otherwise the 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query won't find multiple alarms for the same event. 2743156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // 2744156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // The CAST is needed in the query because otherwise the expression 2745156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will be untyped and sqlite3's manifest typing will not convert the 2746156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // string query parameter to an int in myAlarmtime>=?, so the comparison 2747156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will fail. This could be simplified if bug 2464440 is resolved. 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String query = "SELECT begin-(minutes*60000) AS myAlarmTime," 27499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " Instances.event_id AS eventId, begin, end," 27509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " title, allDay, method, minutes" 27519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 27539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " INNER JOIN Reminders" 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Instances.event_id = Reminders.event_id)" 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE method=" + Reminders.METHOD_ALERT 2756156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime>=CAST(? AS INT)" 2757156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime<=CAST(? AS INT)" 2758156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND end>=?" 27599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " AND 0=(SELECT count(*) from CalendarAlerts CA" 27609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " where CA.event_id=Instances.event_id AND CA.begin=Instances.begin" 27619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " AND CA.alarmTime=myAlarmTime)" 27629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ORDER BY myAlarmTime,begin,title"; 2763156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff String queryParams[] = new String[] {String.valueOf(start), String.valueOf(nextAlarmTime), 2764156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff String.valueOf(currentMillis)}; 27659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff acquireInstanceRangeLocked(start, end, false /* don't use minimum expansion windows */); 27679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 27689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 2769156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff cursor = db.rawQuery(query, queryParams); 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27718f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int beginIndex = cursor.getColumnIndex(Instances.BEGIN); 27728f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int endIndex = cursor.getColumnIndex(Instances.END); 27738f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int eventIdIndex = cursor.getColumnIndex("eventId"); 27748f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int alarmTimeIndex = cursor.getColumnIndex("myAlarmTime"); 27758f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutesIndex = cursor.getColumnIndex(Reminders.MINUTES); 27769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 27789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 27799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(nextAlarmTime); 27809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String alarmTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 27818f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "cursor results: " + cursor.getCount() + " nextAlarmTime: " 27828f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + alarmTimeStr); 27839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (cursor.moveToNext()) { 27869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule all alarms whose alarm time is as early as any 27879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // scheduled alarm. For example, if the earliest alarm is at 27889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 1pm, then we will schedule all alarms that occur at 1pm 27899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // but no alarms that occur later than 1pm. 27909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Actually, we allow alarms up to a minute later to also 27919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be scheduled so that we don't have to check immediately 27929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // again after an event alarm goes off. 27938f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long alarmTime = cursor.getLong(alarmTimeIndex); 27948f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long eventId = cursor.getLong(eventIdIndex); 27958f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutes = cursor.getInt(minutesIndex); 27968f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long startTime = cursor.getLong(beginIndex); 27978f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long endTime = cursor.getLong(endIndex); 27989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 28009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 28019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(alarmTime); 28029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); 28039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(startTime); 28049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 28058f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 28068f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, " looking at id: " + eventId + " " + startTime + startTimeStr 28078f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " alarm: " + alarmTime + schedTime); 28089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmTime < nextAlarmTime) { 28119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff nextAlarmTime = alarmTime; 28129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (alarmTime > 28131edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff nextAlarmTime + DateUtils.MINUTE_IN_MILLIS) { 28149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This event alarm (and all later ones) will be scheduled 28159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // later. 28168f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 28178f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "This event alarm (and all later ones) will be scheduled later"); 28188f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 28199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 28209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Avoid an SQLiteContraintException by checking if this alarm 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // already exists in the table. 28249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (CalendarAlerts.alarmExists(cr, eventId, startTime, alarmTime)) { 28259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 28269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int titleIndex = cursor.getColumnIndex(Events.TITLE); 28279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String title = cursor.getString(titleIndex); 28289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, " alarm exists for id: " + eventId + " " + title); 28299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 28319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Insert this alarm into the CalendarAlerts table 28349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Uri uri = CalendarAlerts.insert(cr, eventId, startTime, 28359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endTime, alarmTime, minutes); 28369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (uri == null) { 28379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.e(TAG, "runScheduleNextAlarm() insert into CalendarAlerts table failed"); 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28418f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, alarmTime); 28429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 28449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28498f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Refresh notification bar 28508f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (rowsDeleted > 0) { 28518f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, currentMillis); 28528f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 28538f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 28549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we scheduled an event alarm, then schedule the next alarm check 28559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for one minute past that alarm. Otherwise, if there were no 28569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarms scheduled, then check again in 24 hours. If a new 28579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event is inserted before the next alarm check, then this method 28589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will be run again when the new event is inserted. 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (nextAlarmTime != Long.MAX_VALUE) { 28601edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(nextAlarmTime + DateUtils.MINUTE_IN_MILLIS); 28619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 28621edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(currentMillis + DateUtils.DAY_IN_MILLIS); 28639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 28679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Removes the entries in the CalendarAlerts table for alarms that we have 28689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled but that have not fired yet. We do this to ensure that we 28699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * don't miss an alarm. The CalendarAlerts table keeps track of the 28709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms that we have scheduled but the actual alarm list is in memory 28719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and will be cleared if the phone reboots. 28729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 28739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We don't need to remove entries that have already fired, and in fact 28749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we should not remove them because we need to display the notifications 28759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * until the user dismisses them. 28769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 28779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We could remove entries that have fired and been dismissed, but we leave 28789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * them around for a while because it makes it easier to debug problems. 28799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Entries that are old enough will be cleaned up later when we schedule 28809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * new alarms. 28819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 28829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void removeScheduledAlarmsLocked(SQLiteDatabase db) { 28839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 28849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "removing scheduled alarms"); 28859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete(CalendarAlerts.TABLE_NAME, 28879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.STATE + "=" + CalendarAlerts.SCHEDULED, null /* whereArgs */); 28889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sEventsTable = "Events"; 28919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sAttendeesTable = "Attendees"; 28929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sRemindersTable = "Reminders"; 28939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sCalendarAlertsTable = "CalendarAlerts"; 28949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sExtendedPropertiesTable = "ExtendedProperties"; 28959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 28979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 28989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 28999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int DELETED_EVENTS = 4; 29009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS = 5; 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_ID = 6; 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES = 7; 29039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES_ID = 8; 29049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS = 9; 29059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS_ID = 10; 29069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES = 11; 29079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES_ID = 12; 29089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS = 13; 29099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_ID = 14; 29109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_BY_INSTANCE = 15; 29116db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int INSTANCES_BY_DAY = 16; 29126db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE = 17; 29136db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE_ID = 18; 29146db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES = 19; 29156db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES_ID = 20; 29166db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_DAYS = 21; 291783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM = 22; 291883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM_REMOVE = 23; 291948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik private static final int TIME = 24; 29209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 29229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 29239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sEventsProjectionMap; 292419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 29259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 29269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 29279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 29289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 2930b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/when/*/*", INSTANCES); 2931b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 2932b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 2933b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events", EVENTS); 2934b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events/#", EVENTS_ID); 2935b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities", EVENT_ENTITIES); 2936b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 2937b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars", CALENDARS); 2938b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars/#", CALENDARS_ID); 2939b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "deleted_events", DELETED_EVENTS); 2940b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees", ATTENDEES); 2941b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees/#", ATTENDEES_ID); 2942b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders", REMINDERS); 2943b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders/#", REMINDERS_ID); 2944b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 2945b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties/#", EXTENDED_PROPERTIES_ID); 2946b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 2947b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 2948b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/by_instance", 2949b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 2950c4e53191b570e09959c5723f4d253977ba48f2d0Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate", SYNCSTATE); 295183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 295283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_PATH, SCHEDULE_ALARM); 295383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 295448587d3291c4db7f0942e1bff55b88cfa7764ba0Erik sUriMatcher.addURI(Calendar.AUTHORITY, "time/#", TIME); 2955997e2e5cb006682bc1a82441304994b458d9745dErik sUriMatcher.addURI(Calendar.AUTHORITY, "time", TIME); 29569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 29589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 29599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HTML_URI, "htmlUri"); 29609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TITLE, "title"); 29619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 29629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DESCRIPTION, "description"); 29639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.STATUS, "eventStatus"); 29649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 29659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 29669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTSTART, "dtstart"); 29679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTEND, "dtend"); 29689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DURATION, "duration"); 29709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ALL_DAY, "allDay"); 29719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.VISIBILITY, "visibility"); 29729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TRANSPARENCY, "transparency"); 29739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 29749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 29759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RRULE, "rrule"); 29769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RDATE, "rdate"); 29779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXRULE, "exrule"); 29789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXDATE, "exdate"); 29799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 29809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 29819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 29829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.LAST_DATE, "lastDate"); 29839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 29849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 29859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 29869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 29879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 29889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORGANIZER, "organizer"); 29897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff sEventsProjectionMap.put(Events.DELETED, "deleted"); 29909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2991e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 29921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 29931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 29941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 2996982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.COLOR, "color"); 2997982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.ACCESS_LEVEL, "access_level"); 2998982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.SELECTED, "selected"); 29999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.URL, "url"); 30009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.TIMEZONE, "timezone"); 30019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, "ownerAccount"); 30029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3003982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 3004e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 3005e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 3006982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3007e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3008982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 30091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._ID, "_id"); 30101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ID, "_sync_id"); 30111ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_VERSION, "_sync_version"); 30121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_TIME, "_sync_time"); 3013c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventsProjectionMap.put(Events._SYNC_DATA, "_sync_local_id"); 30141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_DIRTY, "_sync_dirty"); 30151ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT, "_sync_account"); 30169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT_TYPE, 30171ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff "_sync_account_type"); 30189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 301946f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 302019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HTML_URI, "htmlUri"); 302119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TITLE, "title"); 302219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DESCRIPTION, "description"); 302319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 302419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.STATUS, "eventStatus"); 302519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 302619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 302719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTSTART, "dtstart"); 302819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTEND, "dtend"); 302919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DURATION, "duration"); 303019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 303119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ALL_DAY, "allDay"); 303219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.VISIBILITY, "visibility"); 303319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TRANSPARENCY, "transparency"); 303419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 303519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 303619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RRULE, "rrule"); 303719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RDATE, "rdate"); 303819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXRULE, "exrule"); 303919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXDATE, "exdate"); 304019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 304119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 304219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 304319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.LAST_DATE, "lastDate"); 304419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 304519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 304619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 304719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 304819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 304919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORGANIZER, "organizer"); 305019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DELETED, "deleted"); 305119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 305219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 3053c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventEntitiesProjectionMap.put(Events._SYNC_DATA, Events._SYNC_DATA); 305419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_VERSION, Events._SYNC_VERSION); 305519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_DIRTY, Events._SYNC_DIRTY); 305619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Calendars.URL, "url"); 305719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 30589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 30599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 30609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 30619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 30629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 30639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 30649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 30659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 30669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 30679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 30699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 30709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 30729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 30739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 30749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 30759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 30769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 30789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 30799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 30809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 30819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 30829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 30849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 30859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 30869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 30879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 30889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 30899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 30909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 30919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 30949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make sure that there are no entries for accounts that no longer 30959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * exist. We are overriding this since we need to delete from the 30969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 30977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 30987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 30999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 31009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 31019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb = mDbHelper.getWritableDatabase(); 31029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (mDb == null) return; 31039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 310446f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 310546f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 31069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 31079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 31089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 31099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 31119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 31139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 31149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (String table : new String[]{"Calendars"}) { 31169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Find all the accounts the contacts DB knows about, mark the ones that aren't 31179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 31189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor c = mDb.rawQuery("SELECT DISTINCT " + CalendarDatabaseHelper.ACCOUNT_NAME 31199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "," 31209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + CalendarDatabaseHelper.ACCOUNT_TYPE + " from " 31219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + table, null); 31229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 31239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c.getString(0) != null && c.getString(1) != null) { 31249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 31259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 31269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 31279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 31319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 31349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "removing data for removed account " + account); 31359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 31369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.execSQL("DELETE FROM Calendars" 31379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE " + CalendarDatabaseHelper.ACCOUNT_NAME + "= ? AND " 31389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + CalendarDatabaseHelper.ACCOUNT_TYPE 31399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "= ?", params); 31409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 31429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 31439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 31449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 31459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3148595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /* package */ static boolean readBooleanQueryParameter(Uri uri, String name, 3149595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff boolean defaultValue) { 3150595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String flag = getQueryParameter(uri, name); 31519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return flag == null 31529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ? defaultValue 31539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase())); 31549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3156595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff // Duplicated from ContactsProvider2. TODO: a utility class for shared code 3157595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /** 3158595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff * A fast re-implementation of {@link Uri#getQueryParameter} 3159595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff */ 3160595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /* package */ static String getQueryParameter(Uri uri, String parameter) { 3161595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff String query = uri.getEncodedQuery(); 3162595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (query == null) { 3163595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3164595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3165595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3166595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int queryLength = query.length(); 3167595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int parameterLength = parameter.length(); 3168595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3169595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff String value; 3170595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int index = 0; 3171595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff while (true) { 3172595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index = query.indexOf(parameter, index); 3173595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (index == -1) { 3174595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3175595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3176595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3177595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index += parameterLength; 3178595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3179595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (queryLength == index) { 3180595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3181595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3182595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3183595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (query.charAt(index) == '=') { 3184595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index++; 3185595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff break; 3186595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3187595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3188595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3189595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int ampIndex = query.indexOf('&', index); 3190595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (ampIndex == -1) { 3191595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff value = query.substring(index); 3192595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 3193595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff value = query.substring(index, ampIndex); 3194595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3195595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3196595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return Uri.decode(value); 3197595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3198636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff 3199636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 3200636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 3201636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 3202636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 3203636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 3204636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 3205636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 3206636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 3207636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 3208636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 3209636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 3210636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 3211636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 3212636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 3213636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 3214636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 3215636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 3216636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 3217636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 32189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 3219