CalendarProvider2.java revision 3443e3ebeaa39e8415b43e7cf3b218caee554e9b
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 20830f982e42eafaeb95b72fef9830167d39b025dcErikimport com.google.common.annotations.VisibleForTesting; 21830f982e42eafaeb95b72fef9830167d39b025dcErik 229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.AlarmManager; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.app.PendingIntent; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Debug; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 43f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglioimport android.pim.EventRecurrence; 449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.pim.RecurrenceSet; 459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar; 479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Attendees; 489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.CalendarAlerts; 499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Calendars; 509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Events; 519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Instances; 529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.Calendar.Reminders; 539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 541edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 55192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Config; 579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 59ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 62ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 6568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglioimport java.util.List; 669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.Set; 679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * is defined in {@link android.provider.Calendar}. 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String TAG = "CalendarProvider2"; 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7763c1db9914a7b01181b71cc6c66d046ad7e794a9Erik private static final String TIMEZONE_GMT = "GMT"; 7863c1db9914a7b01181b71cc6c66d046ad7e794a9Erik 799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean PROFILE = false; 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 818f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 828f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff private static final String INVALID_CALENDARALERTS_SELECTOR = 838f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff "_id IN (SELECT ca._id FROM CalendarAlerts AS ca" 848f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " LEFT OUTER JOIN Instances USING (event_id, begin, end)" 858f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " LEFT OUTER JOIN Reminders AS r ON" 868f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " (ca.event_id=r.event_id AND ca.minutes=r.minutes)" 878f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " WHERE Instances.begin ISNULL OR ca.alarmTime<?" 888f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " OR (r.minutes ISNULL AND ca.minutes<>0))"; 898f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 1027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_ORIGINAL_EVENT_INDEX = 3; 1037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 112646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 113646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 114646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 115646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 116646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 117646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 118646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 119646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 120646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 121646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 122646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 123646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 124646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 125646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 126646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 127646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 133ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 1369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private static final Uri SYNCSTATE_CONTENT_URI = Uri.parse("content://syncstate/state"); 13883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // 13983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_URI runs scheduleNextAlarm(false) 14083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // SCHEDULE_ALARM_REMOVE_URI runs scheduleNextAlarm(true) 14183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: use a service to schedule alarms rather than private URI 14283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_PATH = "schedule_alarms"; 14383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; 14483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_URI = 14583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_PATH); 14683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff /* package */ static final Uri SCHEDULE_ALARM_REMOVE_URI = 14783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff Uri.withAppendedPath(Calendar.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); 1489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // To determine if a recurrence exception originally overlapped the 1509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window, we need to assume a maximum duration, since we only know 1519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original start time. 1529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int MAX_ASSUMED_DURATION = 7*24*60*60*1000; 1539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1548ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 1558ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Due to an issue in Calendar Server restricting the length of the name we had to strip it down 1568ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1578ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1588ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1598ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1608ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1613443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 1623443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio EventsRawTimesColumns.EVENT_ID + ", " + 1633443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio EventsRawTimesColumns.DTSTART_2445 + ", " + 1643443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio EventsRawTimesColumns.DTEND_2445 + ", " + 1653443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1663443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 1673443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio "EventsRawTimes" + ", " + 1683443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio "Events" + 1693443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 1703443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio EventsRawTimesColumns.EVENT_ID + " = " + "Events." + Events._ID; 1713443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio 1729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class TimeRange { 1739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean allDay; 1769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesRange { 1799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long begin; 1809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public long end; 1819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public InstancesRange(long begin, long end) { 1839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.begin = begin; 1849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff this.end = end; 1859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class InstancesList 1899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends ArrayList<ContentValues> { 1909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static final class EventInstancesMap 1939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff extends HashMap<String, InstancesList> { 1941030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff public void add(String syncIdKey, ContentValues values) { 1951030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList instances = get(syncIdKey); 1969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (instances == null) { 1979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances = new InstancesList(); 1981030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff put(syncIdKey, instances); 1999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instances.add(values); 2019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A thread that runs in the background and schedules the next 2059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // calendar event alarm. 2069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class AlarmScheduler extends Thread { 2079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean mRemoveAlarms; 2089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public AlarmScheduler(boolean removeAlarms) { 2109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mRemoveAlarms = removeAlarms; 2119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 213192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank @Override 2149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 2159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 2169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 2179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff runScheduleNextAlarm(mRemoveAlarms); 2189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e) { 21952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 22052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "runScheduleNextAlarm() failed", e); 22152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 2229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We search backward in time for event reminders that we may have missed 2289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and schedule them if the event has not yet expired. The amount in 2299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the past to search backwards is controlled by this constant. It 2309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should be at least a few minutes to allow for an event that was 2319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * recently created on the web to make its way to the phone. Two hours 2329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * might seem like overkill, but it is useful in the case where the user 2339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * just crossed into a new timezone and might have just missed an alarm. 2349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2351edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff private static final long SCHEDULE_ALARM_SLACK = 2 * DateUtils.HOUR_IN_MILLIS; 2369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Alarms older than this threshold will be deleted from the CalendarAlerts 2399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. This should be at least a day because if the timezone is 2409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * wrong and the user corrects it we might delete good alarms that 2419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * appear to be old because the device time was incorrectly in the future. 2429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This threshold must also be larger than SCHEDULE_ALARM_SLACK. We add 2439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the SCHEDULE_ALARM_SLACK to ensure this. 2449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 2459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * To make it easier to find and debug problems with missed reminders, 2469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * set this to something greater than a day. 2479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long CLEAR_OLD_ALARM_THRESHOLD = 2491edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff 7 * DateUtils.DAY_IN_MILLIS + SCHEDULE_ALARM_SLACK; 2509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // A lock for synchronizing access to fields that are shared 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // with the AlarmScheduler thread. 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Object mAlarmLock = new Object(); 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allocate the string constant once here instead of on the heap 2649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String CALENDAR_ID_SELECTION = "calendar_id=?"; 2659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sInstancesProjection = 2679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Instances.START_DAY, Instances.END_DAY, 2689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Instances.START_MINUTE, Instances.END_MINUTE, Instances.ALL_DAY }; 2699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager mAlarmManager; 2779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarAppWidgetProvider mAppWidgetProvider = CalendarAppWidgetProvider.getInstance(); 2799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 2829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 2839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 2849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 2869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 2879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 2899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 2919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 2929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 2949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 2959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 2969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 2989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 2999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 3029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 303ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 304ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Columns from the EventsRawTimes table 305ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 306ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public interface EventsRawTimesColumns 307ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio { 308ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 309ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The corresponding event id 310ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: INTEGER (long)</P> 311ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 312ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String EVENT_ID = "event_id"; 313ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 314ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 315ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant time the event starts 316ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 317ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 318ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String DTSTART_2445 = "dtstart2445"; 319ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 320ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 321ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant time the event ends 322ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 323ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 324ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String DTEND_2445 = "dtend2445"; 325ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 326ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 327ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant original instance time of the recurring event for which this 328ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * event is an exception. 329ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 330ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 331ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445"; 332ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 333ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 334ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * The RFC2445 compliant last date this event repeats on, or NULL if it never ends 335ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * <P>Type: TEXT</P> 336ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 337ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio public static final String LAST_DATE_2445 = "lastDate2445"; 338ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 339ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 3409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void verifyAccounts() { 3419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 3429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff onAccountsUpdated(AccountManager.get(getContext()).getAccounts()); 3439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 3469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 3489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 3499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 3539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 3549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 3559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff verifyAccounts(); 3579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 3599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 3609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 3629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 3639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 3649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Context c = getContext(); 3659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 3679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 3689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 3699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 3709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.registerReceiver(mIntentReceiver, filter); 3719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData = new MetaData(mDbHelper); 373ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 374ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 3759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 3769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 3789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 3819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 3829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 38368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio * has changed. 3849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 3869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 3879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 3889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 3919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 3939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 3949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff doUpdateTimezoneDependentFields(); 3969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e) { 39752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 39852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 39952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 4009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 4019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Clear at least the in-memory data (and if possible the 4029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // database fields) to force a re-computation of Instances. 4039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mMetaData.clearInstanceRange(); 4049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (SQLException e2) { 40552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 40652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 40752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 4089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 41468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio * Check if we are in the same time zone 41568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio */ 41668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 41768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 41868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 41968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 42068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 42168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio /** 42268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio * This method runs in a background thread. If the timezone db or timezone has changed 4239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 4249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 42568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 42668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 42768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 42868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 42968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return; 43068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 43168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 432ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 43368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 43468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 435ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 43668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 4379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Even if the timezone hasn't changed, check for missed alarms. 4389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This code executes when the CalendarProvider2 is created and 4399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // helps to catch missed alarms when the Calendar process is 4409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // killed (because of low-memory conditions) and then restarted. 4419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 4429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 443ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 444ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 44568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 446ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 447ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (mDb == null) { 448ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 449ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Cannot update Events table from EventsRawTimes table"); 450ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 451ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return; 452ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 453ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 454ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 4553443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 456ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 45768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 458ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 459ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 460ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 461ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 462ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 463ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 464ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 4653443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 4663443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 467ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 468ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 469ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 470ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 471ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 4723443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 47352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 47452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 47552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 47652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "at the same time in EventsRawTimes!"); 47752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 47852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio continue; 47952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 480ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 4813443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 482ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 483ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 484ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 485ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 486ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 487ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 488ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 489ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 490ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 491ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 492ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 49352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 49452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.v( TAG, "Cannot parse null RFC2445 date"); 49552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 496ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 497ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 498ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 499ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 500ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 501ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 50252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 50352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e( TAG, "Cannot parse RFC2445 date " + dt2445); 50452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 505ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 506ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 507ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 508ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 509ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 510ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 511ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 512ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 513ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 514ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtstart", get2445ToMillis(timezone, dtStart2445)); 515ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio values.put("dtend", get2445ToMillis(timezone, dtEnd2445)); 516ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 517dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff int result = mDb.update("Events", values, "_id=?", 518dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 519ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 520ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 521ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 522ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 523ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 524ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 525ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 526ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 527ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 528ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 529ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 53052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 53152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 53252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 533ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 534ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 536ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 537ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 538ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 539ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 54068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 54168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (timezoneDatabaseVersion == null) { 542ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 543ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 544ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 545ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 546ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 54725e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 548ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 54968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 55068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (timezoneDatabaseVersion == null) { 551ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 552ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 55352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 55452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 55552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 556ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 557ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 558ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 55968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private boolean isHomeTimezone() { 56068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 56168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 56268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 56368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 564ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 5659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 5669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 5679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 5689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 56968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 57068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio Time time = new Time(instancesTimezone); 5719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 5729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 5739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 5749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 5759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 5761f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 5779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 5789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 5791f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 5801f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 5811f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 5821f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 5831f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 5841f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 585d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio null /* selection */, null /* sort */, 586d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 58768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio true /* force Instances deletion and expansion */, 58868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone, 58968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio isHomeTimezone()); 5901f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 5911f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 5921f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 5931f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 5941f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 5959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rescheduleMissedAlarms(); 5979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void rescheduleMissedAlarms() { 6009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 6019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager != null) { 6029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 6039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = context.getContentResolver(); 6049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.rescheduleMissedAlarms(cr, context, manager); 6059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 6099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Appends comma separated ids. 6109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param ids Should not be empty 6119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 6129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void appendIds(StringBuilder sb, HashSet<Long> ids) { 6139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long id : ids) { 6149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.append(id).append(','); 6159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sb.setLength(sb.length() - 1); // Yank the last comma 6189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 6219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void notifyChange() { 6229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 6239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 6249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff getContext().getContentResolver().notifyChange(Calendar.CONTENT_URI, null, 6259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff true /* syncToNetwork */); 6269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 6299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 6309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 631ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 632ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 6339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 6369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 6389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 6399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 64068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String instancesTimezone; 6419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 6439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 6449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 6459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 6469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 6479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 6491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 6509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 651595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 6529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 6541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 6559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 656636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 657636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 6589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 65919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 66019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 66119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 66219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 663595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 66419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 66519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 66619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 66719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 668636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 669636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 67019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 67119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 6729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 6739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 674595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff appendAccountFromParameter(qb, uri); 6759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 6779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Calendars"); 678636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 679636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("_id=?"); 6809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 6819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 6829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 6839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 6849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 6859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 6879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 6899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 6909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 6929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 6939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 6949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 6959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 6969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 69768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 6989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return handleInstanceQuery(qb, begin, end, projection, 699d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio selection, sortOrder, match == INSTANCES_BY_DAY, 70068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio false /* do not force Instances deletion and expansion */, 70168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone, isHomeTimezone()); 7026db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 7039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 7049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 7059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 7069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 7079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 7089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 7099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 7109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 7129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 7139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 7149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 7159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 7169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 71768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 71868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 71968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone, isHomeTimezone()); 7209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 7211ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 7229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 7231ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.appendWhere("Events._id=Attendees.event_id"); 7249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 7261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Attendees, Events"); 7279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 728636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 729636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Attendees._id=? AND Events._id=Attendees.event_id"); 7309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 7329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Reminders"); 7339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 7351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables("Reminders, Events"); 7369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 737636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 738636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("Reminders._id=? AND Events._id=Reminders.event_id"); 7399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 741e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 7429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 743e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 744e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 7459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 747e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 7489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 749e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 750e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id"); 7519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 7529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 754e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.setTables("CalendarAlerts, " + CalendarDatabaseHelper.Views.EVENTS); 7559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 756636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 757e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff qb.appendWhere(CalendarDatabaseHelper.Views.EVENTS + 758e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff "._id=CalendarAlerts.event_id AND CalendarAlerts._id=?"); 7599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 7619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("ExtendedProperties"); 7629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 7647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff qb.setTables("ExtendedProperties"); 765636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 766636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff qb.appendWhere("ExtendedProperties._id=?"); 7679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 76868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio case PROVIDER_PROPERTIES: 76968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio qb.setTables("CalendarCache"); 77068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 77168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio break; 7729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 7739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 7749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 7779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 7789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 7819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 7829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 783ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 784ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 785ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 786ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 787ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 788ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 789ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 790ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 791ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 7929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 7939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 7949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.setNotificationUri(getContext().getContentResolver(), Calendar.Events.CONTENT_URI); 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 812d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 81368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 81476f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 8169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 8179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 818d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio long rangeEnd, String[] projection, String selection, String sort, 81968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean searchByDay, boolean forceExpansion, String instancesTimezone, 82068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean isHomeTimezone) { 8219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setTables("Instances INNER JOIN Events ON (Instances.event_id=Events._id) " + 8239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff "INNER JOIN Calendars ON (Events.calendar_id = Calendars._id)"); 8249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 8269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 82868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio Time time = new Time(instancesTimezone); 8299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 8309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 8319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 8329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 8339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 83468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 83568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone 836d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio ); 8378335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("startDay<=? AND endDay>=?"); 8389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 8399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 84068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 84168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone 842d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio ); 8438335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("begin<=? AND end>=?"); 8449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8458335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(rangeEnd), 8468335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 8478335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 8487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 8499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8516db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 85268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String[] projection, String selection, String instancesTimezone, 85368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean isHomeTimezone) { 8546db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setTables("Instances INNER JOIN Events ON (Instances.event_id=Events._id) " + 8556db535b458146a279bebd4a51d56c1bdfc204528Erik "INNER JOIN Calendars ON (Events.calendar_id = Calendars._id)"); 8566db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 85743556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 85843556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 85976f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio Time time = new Time(instancesTimezone); 860192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 86143556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 86243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 863192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 86443556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 86568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 86668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 8678335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff qb.appendWhere("startDay<=? AND endDay>=?"); 8688335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 8698335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 8708335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 8716db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. Acquires the database lock and calls {@link #acquireInstanceRangeLocked}. 8779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 881d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 88276f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 88376f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 8849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 885d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 88668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 88768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 8889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 8899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 89068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 89168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 8929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 8939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 8949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 8959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 8999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 9009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 9019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 9029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 9039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 9049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 90576f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 90676f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 90776f4f0375fa5892d3b2ab0c39d1fc367528b974cFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 90968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 91068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 9119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 91468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (instancesTimezone == null) { 91552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 91652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() " 91752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "because instancesTimezone is null"); 91852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 91968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return; 92068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 92168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 9239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 9259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 9279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 94068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean timezoneChanged; 94168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (isHomeTimezone) { 94268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 94368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 94468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } else { 94568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 94668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 94763c1db9914a7b01181b71cc6c66d046ad7e794a9Erik // if we're in auto make sure we are using the device time zone 94863c1db9914a7b01181b71cc6c66d046ad7e794a9Erik if (timezoneChanged) { 94963c1db9914a7b01181b71cc6c66d046ad7e794a9Erik instancesTimezone = localTimezone; 95063c1db9914a7b01181b71cc6c66d046ad7e794a9Erik } 95168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 95268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if "home", then timezoneChanged only if current != previous 95368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 954d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.execSQL("DELETE FROM Instances;"); 95752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 9586db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 9609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 96168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 96268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 96368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 96568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 96663c1db9914a7b01181b71cc6c66d046ad7e794a9Erik // This may cause some double writes but guarantees the time zone in 96763c1db9914a7b01181b71cc6c66d046ad7e794a9Erik // the db and the time zone the instances are in is the same, which 96863c1db9914a7b01181b71cc6c66d046ad7e794a9Erik // future changes may affect. 96963c1db9914a7b01181b71cc6c66d046ad7e794a9Erik mCalendarCache.writeTimezoneInstances(instancesTimezone); 97063c1db9914a7b01181b71cc6c66d046ad7e794a9Erik 97163c1db9914a7b01181b71cc6c66d046ad7e794a9Erik // If we're in auto check if we need to fix the previous tz value 97268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 97363c1db9914a7b01181b71cc6c66d046ad7e794a9Erik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 97463c1db9914a7b01181b71cc6c66d046ad7e794a9Erik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 97563c1db9914a7b01181b71cc6c66d046ad7e794a9Erik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 97663c1db9914a7b01181b71cc6c66d046ad7e794a9Erik } 97768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 9839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 9849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 9859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 9869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 9879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 9889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 9899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 9909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 9919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 9929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 99352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 9949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 9959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 9969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 9989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 10029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 100368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 10049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 10089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 10099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 101068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 101468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Update the bounds on the Instances table (timezone is the same here) 101568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 10169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EXPAND_COLUMNS = new String[] { 10199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._ID, 10209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 10219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.STATUS, 10229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTSTART, 10239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DTEND, 10249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EVENT_TIMEZONE, 10259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 10269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 10279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXRULE, 10289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.EXDATE, 10299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.DURATION, 10309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ALL_DAY, 10319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.ORIGINAL_EVENT, 10321030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff Events.ORIGINAL_INSTANCE_TIME, 10331dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio Events.CALENDAR_ID, 10341dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio Events.DELETED 10359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 10369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make instances for the given range. 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void expandInstanceRangeLocked(long begin, long end, String localTimezone) { 10419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 10439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.startMethodTracing("expandInstanceRangeLocked"); 10449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 10479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Expanding events between " + begin + " and " + end); 10489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getEntries(begin, end); 10519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 10529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff performInstanceExpansion(begin, end, localTimezone, entries); 10539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 10549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 10559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 10569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (PROFILE) { 10599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Debug.stopMethodTracing(); 10609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 10649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Get all entries affecting the given window. 10659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 10669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 10679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return Cursor for the entries; caller must close it. 10689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getEntries(long begin, long end) { 10709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 10711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 10729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 10739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String beginString = String.valueOf(begin); 10759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String endString = String.valueOf(end); 10769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // grab recurrence exceptions that fall outside our expansion window but modify 10789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences that do fall within our window. we won't insert these into the output 10799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set of instances, but instead will just add them to our cancellations list, so we 10809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // can cancel the correct recurrence expansion instances. 10819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we don't have originalInstanceDuration or end time. for now, assume the original 10829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance lasts no longer than 1 week. 10832d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio // also filter with syncable state (we dont want the entries from a non syncable account) 10849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: compute the originalInstanceEndTime or get this from the server. 10852d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio qb.appendWhere("((dtstart <= ? AND (lastDate IS NULL OR lastDate >= ?)) OR " + 10868335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff "(originalInstanceTime IS NOT NULL AND originalInstanceTime <= ? AND " + 10872d1b3d70a6ebce8194932f8a8355d97a89da113fFabrice Di Meglio "originalInstanceTime >= ?)) AND (sync_events != 0)"); 10888335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {endString, beginString, endString, 10898335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(begin - MAX_ASSUMED_DURATION)}; 1090e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Cursor c = qb.query(mDb, EXPAND_COLUMNS, null /* selection */, 10918335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff selectionArgs, null /* groupBy */, 10927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 1093e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 1094e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.v(TAG, "Instance expansion: got " + c.getCount() + " entries"); 1095e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1096e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff return c; 10979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 11001030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * Generates a unique key from the syncId and calendarId. 11011030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * The purpose of this is to prevent collisions if two different calendars use the 11021030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * same sync id. This can happen if a Google calendar is accessed by two different accounts, 11031030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * or with Exchange, where ids are not unique between calendars. 11041030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param syncId Id for the event 11051030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @param calendarId Id for the calendar 11061030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff * @return key 11071030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff */ 11081030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff private String getSyncIdKey(String syncId, long calendarId) { 11091030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff return calendarId + ":" + syncId; 11101030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff } 11111030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 11121030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff /** 11139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Perform instance expansion on the given entries. 11149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin Window start (ms). 11159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end Window end (ms). 11169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param localTimezone 11179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param entries The entries to process. 11189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 11199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void performInstanceExpansion(long begin, long end, String localTimezone, 11209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries) { 11219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 11229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11231030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // Key into the instance values to hold the original event concatenated with calendar id. 11241030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff final String ORIGINAL_EVENT_AND_CALENDAR = "ORIGINAL_EVENT_AND_CALENDAR"; 11251030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff 11269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int statusColumn = entries.getColumnIndex(Events.STATUS); 11279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtstartColumn = entries.getColumnIndex(Events.DTSTART); 11289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int dtendColumn = entries.getColumnIndex(Events.DTEND); 11299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int eventTimezoneColumn = entries.getColumnIndex(Events.EVENT_TIMEZONE); 11309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int durationColumn = entries.getColumnIndex(Events.DURATION); 11319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rruleColumn = entries.getColumnIndex(Events.RRULE); 11329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rdateColumn = entries.getColumnIndex(Events.RDATE); 11339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exruleColumn = entries.getColumnIndex(Events.EXRULE); 11349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int exdateColumn = entries.getColumnIndex(Events.EXDATE); 11359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int allDayColumn = entries.getColumnIndex(Events.ALL_DAY); 11369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int idColumn = entries.getColumnIndex(Events._ID); 11379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int syncIdColumn = entries.getColumnIndex(Events._SYNC_ID); 11389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalEventColumn = entries.getColumnIndex(Events.ORIGINAL_EVENT); 11399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int originalInstanceTimeColumn = entries.getColumnIndex(Events.ORIGINAL_INSTANCE_TIME); 11401030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff int calendarIdColumn = entries.getColumnIndex(Events.CALENDAR_ID); 11411dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio int deletedColumn = entries.getColumnIndex(Events.DELETED); 11429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues initialValues; 11449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff EventInstancesMap instancesMap = new EventInstancesMap(); 11459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 11479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time eventTime = new Time(); 11489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: entries contains all events that affect the current 11509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window. It consists of: 11519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. These will be 11529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed. 11539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Recurrences that included the window. These will be displayed 11549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if not canceled. 11559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Recurrence exceptions that fall in the window. These will be 11569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // displayed if not cancellations. 11579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 11589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window (subject to 1 week assumption above), but are outside 11599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the window. These will not be displayed. Cases c and d are 11609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // distingushed by the start / end time. 11619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (entries.moveToNext()) { 11639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 11649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = null; 11659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = entries.getInt(allDayColumn) != 0; 11679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String eventTimezone = entries.getString(eventTimezoneColumn); 11699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(eventTimezone)) { 11709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the events table, allDay events start at midnight. 11719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this forces them to stay at midnight for all day events 11729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: check that this actually does the right thing. 11739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTimezone = Time.TIMEZONE_UTC; 11749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = entries.getLong(dtstartColumn); 11779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long eventId = Long.valueOf(entries.getLong(idColumn)); 11789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = entries.getString(durationColumn); 11809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 11819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 11829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 11839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff catch (DateException e) { 118552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 118652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "error parsing duration for event " 118752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + eventId + "'" + durationStr + "'", e); 118852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 11899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 11909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 11919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 11929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 11939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 11949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 11959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 11969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String syncId = entries.getString(syncIdColumn); 12009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = entries.getString(originalEventColumn); 12019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalInstanceTimeMillis = -1; 12039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(originalInstanceTimeColumn)) { 12049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis= entries.getLong(originalInstanceTimeColumn); 12059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = entries.getInt(statusColumn); 12071dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio boolean deleted = (entries.getInt(deletedColumn) != 0); 12089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rruleStr = entries.getString(rruleColumn); 12109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String rdateStr = entries.getString(rdateColumn); 12119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exruleStr = entries.getString(exruleColumn); 12129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String exdateStr = entries.getString(exdateColumn); 12131030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff long calendarId = entries.getLong(calendarIdColumn); 12141030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String syncIdKey = getSyncIdKey(syncId, calendarId); // key into instancesMap 12159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1216f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 1217f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 1218f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(rruleStr, rdateStr, exruleStr, exdateStr); 1219f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 122052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 122152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + rruleStr, e); 122252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 1223f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio continue; 1224f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 12259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1226f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 12279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating 12289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (status == Events.STATUS_CANCELED) { 12309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen! 123152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 123252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Found canceled recurring event in " 123352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "Events table. Ignoring."); 123452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 12359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 12369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // need to parse the event into a local calendar. 12399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = eventTimezone; 12409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.set(dtstartMillis); 12419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.allDay = allDay; 12429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 12449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen. 124552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 124652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Repeating event has no duration -- " 124752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "should not happen."); 124852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 12499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 12509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // set to one day. 12519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 12529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 1; 12549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 12559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 12569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 12579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P1D"; 12589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 12599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // compute the duration from dtend, if we can. 12609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // otherwise, use 0s. 12619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.sign = 1; 12629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.weeks = 0; 12639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.days = 0; 12649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.hours = 0; 12659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.minutes = 0; 12669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 12679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = entries.getLong(dtendColumn); 12689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = (int) ((dtendMillis - dtstartMillis) / 1000); 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P" + duration.seconds + "S"; 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 12719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.seconds = 0; 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff durationStr = "+P0S"; 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long[] dates; 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dates = rp.expand(eventTime, recur, begin, end); 12799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Initialize the "eventTime" timezone outside the loop. 12819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is used in computeTimezoneDependentFields(). 12829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 12839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 12849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 12859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 12869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long durationMillis = duration.getMillis(); 12899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (long date : dates) { 12909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 12919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 12929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.BEGIN, date); 12949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = date + durationMillis; 12959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 12969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(date, dtendMillis, 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 12991030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 13009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 13029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating 13039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues = new ContentValues(); 13049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if this event has an "original" field, then record 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that we need to cancel the original event (we can't 13079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do that here because the order of this loop isn't 13089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // defined) 13099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 13101030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // The ORIGINAL_EVENT_AND_CALENDAR holds the 13111030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // calendar id concatenated with the ORIGINAL_EVENT to form 13121030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // a unique key, matching the keys for instancesMap. 13131030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff initialValues.put(ORIGINAL_EVENT_AND_CALENDAR, 13141030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff getSyncIdKey(originalEvent, calendarId)); 13159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.ORIGINAL_INSTANCE_TIME, 13169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTimeMillis); 13179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, status); 13189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtendMillis = dtstartMillis; 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr == null) { 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!entries.isNull(dtendColumn)) { 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = entries.getLong(dtendColumn); 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = duration.addTo(dtstartMillis); 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this non-recurring event might be a recurrence exception that doesn't 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // actually fall within our expansion window, but instead was selected 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so we can correctly cancel expanded recurrence instances below. do not 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // add events to the instances map if they don't actually fall within our 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansion window. 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((dtendMillis < begin) || (dtstartMillis > end)) { 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null && originalInstanceTimeMillis != -1) { 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Events.STATUS, Events.STATUS_CANCELED); 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 133852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 133952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Unexpected event outside window: " + syncId); 134052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.EVENT_ID, eventId); 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13471dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio initialValues.put(Instances.BEGIN, dtstartMillis); 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff initialValues.put(Instances.END, dtendMillis); 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13501dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // we temporarily store the DELETED status (will be cleaned later) 13511dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio initialValues.put(Events.DELETED, deleted); 13521dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 13549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = Time.TIMEZONE_UTC; 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 13569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime.timezone = localTimezone; 13579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, 13599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff eventTime, initialValues); 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13611030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff instancesMap.add(syncIdKey, initialValues); 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 136452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 136552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 136652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (TimeFormatException e) { 136852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 136952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "RecurrenceProcessor error ", e); 137052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 13719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains all instances that affect the 13751030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // window, indexed by original sync id concatenated with calendar id. 13761030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // It consists of: 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. They have: 13789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 13799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window. They may 13809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be subject to exceptions. They have: 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // EVENT_ID, BEGIN, END 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They have: 13831030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS (since they can 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be a modification or cancellation), EVENT_ID, BEGIN, END 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. They have: 13871030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff // ORIGINAL_EVENT_AND_CALENDAR, ORIGINAL_INSTANCE_TIME, STATUS = 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED, EVENT_ID, BEGIN, END 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // First, delete the original instances corresponding to recurrence 13919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exceptions. We do this by iterating over the list and for each 13929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrence exception, we search the list for an instance with a 13939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // matching "original instance time". If we find such an instance, 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we remove it from the list. If we don't find such an instance 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then we cancel the recurrence exception. 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Set<String> keys = instancesMap.keySet(); 13971030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 13981030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 13999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 14009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If this instance is not a recurrence exception, then 14029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // skip it. 14031030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff if (!values.containsKey(ORIGINAL_EVENT_AND_CALENDAR)) { 14049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 14059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14071030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff String originalEventPlusCalendar = values.getAsString(ORIGINAL_EVENT_AND_CALENDAR); 14089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long originalTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 14091030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList originalList = instancesMap.get(originalEventPlusCalendar); 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalList == null) { 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The original recurrence is not present, so don't try canceling it. 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 14139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Search the original event for a matching original 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance time. If there is a matching one, then remove 14179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the original one. We do this both for exceptions that 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // change the original instance as well as for exceptions 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that delete the original instance. 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (int num = originalList.size() - 1; num >= 0; num--) { 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues originalValues = originalList.get(num); 14229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginTime = originalValues.getAsLong(Instances.BEGIN); 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (beginTime == originalTime) { 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We found the original instance, so remove it. 14259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalList.remove(num); 14269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Invariant: instancesMap contains filtered instances. 14329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // It consists of: 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Individual events that fall in the window. 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Instances of recurrences that fall in the window and have not 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // been subject to exceptions. 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // c) Exceptions that fall in the window. They will have 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // STATUS_CANCELED if they are cancellations. 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // d) Recurrence exceptions that modify an instance inside the 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // window but fall outside the window. These are STATUS_CANCELED. 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do the inserts. Since the db lock is held when this method is executed, 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // this will be done in a transaction. 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if there is lock contention (e.g., a sync is trying to merge into the db 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // while the calendar app is trying to query the db (expanding instances)), we will 14459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // not be "polite" and yield the lock until we're done. This will favor local query 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // operations over sync/write operations. 14471030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff for (String syncIdKey : keys) { 14481030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff InstancesList list = instancesMap.get(syncIdKey); 14499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (ContentValues values : list) { 14509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14511dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // If this instance was cancelled or deleted then don't create a new 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // instance. 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer status = values.getAsInteger(Events.STATUS); 14541dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio boolean deleted = values.containsKey(Events.DELETED) ? 14551dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio values.getAsBoolean(Events.DELETED) : false; 14561dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio if ((status != null && status == Events.STATUS_CANCELED) || deleted) { 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14601dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio // We remove this useless key (not valid in the context of Instances table) 14611dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio values.remove(Events.DELETED); 14621dc2919361bc56af0c5ea763845fae49d289839aFabrice Di Meglio 14639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Remove these fields before inserting a new instance 14641030436aca59c5123ac90e325e8dbd7e04143909Ken Shirriff values.remove(ORIGINAL_EVENT_AND_CALENDAR); 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.remove(Events.STATUS); 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1468c874ed5c6cc0fcc6ac06ae7d20db0eab7d749608Ken Shirriff mDbHelper.instancesReplace(values); 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Computes the timezone-dependent fields of an instance of an event and 14759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * updates the "values" map to contain those fields. 14769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 14779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin the start time of the instance (in UTC milliseconds) 14789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end the end time of the instance (in UTC milliseconds) 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param local a Time object with the timezone set to the local timezone 14809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values a map that will contain the timezone-dependent fields 14819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 14829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void computeTimezoneDependentFields(long begin, long end, 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local, ContentValues values) { 14849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(begin); 14859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay = Time.getJulianDay(begin, local.gmtoff); 14869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startMinute = local.hour * 60 + local.minute; 14879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.set(end); 14899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay = Time.getJulianDay(end, local.gmtoff); 14909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endMinute = local.hour * 60 + local.minute; 14919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Special case for midnight, which has endMinute == 0. Change 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that to +24 hours on the previous day to make everything simpler. 14949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Exception: if start and end minute are both 0 on the same day, 14959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // then leave endMinute alone. 14969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (endMinute == 0 && endDay > startDay) { 14979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endMinute = 24 * 60; 14989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay -= 1; 14999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_DAY, startDay); 15029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_DAY, endDay); 15039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.START_MINUTE, startMinute); 15049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Instances.END_MINUTE, endMinute); 15059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 15089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 15099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 15109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 15119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 15129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 15139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 15149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 15159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 15169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 15179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 15189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 15199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 15209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 15219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 15229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 15239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 15249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 15259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 15269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 15276db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 15289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 152948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 153048587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 153168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio case PROVIDER_PROPERTIES: 153268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return "vnd.android.cursor.dir/property"; 15339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 15349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 15359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 15389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public static boolean isRecurrenceEvent(ContentValues values) { 15399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return (!TextUtils.isEmpty(values.getAsString(Events.RRULE))|| 15409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.RDATE))|| 15419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT))); 15429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1544646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1545646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1546646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1547646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1548646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * corrects the fields DTSTART, DTEND, and DURATION if necessary. Also checks to ensure that 1549646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * either both DTSTART and DTEND or DTSTART and DURATION are set for each event. 1550646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1551646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @param updatedValues The values to check and correct 1552646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1553646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1554646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private boolean fixAllDayTime(Uri uri, ContentValues updatedValues) { 1555646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1556646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (updatedValues.containsKey(Events.ALL_DAY) 1557646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik && updatedValues.getAsInteger(Events.ALL_DAY).intValue() == 1) { 1558646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtstart = updatedValues.getAsLong(Events.DTSTART); 1559646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtend = updatedValues.getAsLong(Events.DTEND); 1560646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String duration = updatedValues.getAsString(Events.DURATION); 1561646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Time time = new Time(); 1562646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Cursor currentTimesCursor = null; 1563646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String tempValue; 1564646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If a complete set of time fields doesn't exist query the db for them. A complete set 1565646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // is dtstart and dtend for non-recurring events or dtstart and duration for recurring 1566646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // events. 1567646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if(dtstart == null || (dtend == null && duration == null)) { 1568646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we have an id to search for, if not this is probably a new event 1569646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 2) { 1570646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = query(uri, 1571646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik ALLDAY_TIME_PROJECTION, 1572646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selection */, 1573646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selectionArgs */, 1574646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* sort */); 1575646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1576646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (!currentTimesCursor.moveToFirst() || 1577646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.getCount() != 1) { 1578646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Either this is a new event or the query is too general to get data 1579646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // from the db. In either case don't try to use the query and catch 1580646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // errors when trying to update the time fields. 1581646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1582646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = null; 1583646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1584646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1585646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1586646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1587646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1588646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Ensure dtstart exists for this event (always required) and set so h,m,s are 0 if 1589646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // necessary. 1590646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // TODO Move this somewhere to check all events, not just allDay events. 1591646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtstart == null) { 1592646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1593646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields, we'd like to know if a field is empty 1594646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1595646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTSTART_INDEX); 1596646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1597646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtstart = Long.valueOf(tempValue); 1598646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1599646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1600646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("Event has no DTSTART field, the db " + 1601646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "may be damaged. Set DTSTART for this event to fix."); 1602646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1603646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 1604646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTSTART cannot be empty for new events."); 1605646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1606646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1607646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1608646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtstart.longValue()); 1609646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1610646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1611646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1612646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1613646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTSTART, time.toMillis(true)); 1614646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1615646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1616646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1617646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If dtend exists for this event make sure it's h,m,s are 0. 1618646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend == null && currentTimesCursor != null) { 1619646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields. We'd like to know if a field is empty 1620646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1621646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTEND_INDEX); 1622646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1623646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = Long.valueOf(tempValue); 1624646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1625646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = null; 1626646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1627646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1628646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend != null) { 1629646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1630646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtend.longValue()); 1631646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1632646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1633646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1634646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1635646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = time.toMillis(true); 1636646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTEND, dtend); 1637646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1638646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1639646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1640646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1641646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1642646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null) { 1643646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = currentTimesCursor.getString(ALLDAY_DURATION_INDEX); 1644646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1645646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1646646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1647646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1648646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration != null) { 1649646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int len = duration.length(); 1650646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1651646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * in the seconds format, and if so converts it to days. 1652646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1653646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (len == 0) { 1654646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = null; 1655646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else if (duration.charAt(0) == 'P' && 1656646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration.charAt(len - 1) == 'S') { 1657646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1658646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1659646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = "P" + days + "D"; 1660646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DURATION, duration); 1661646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1662646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else if (duration.charAt(0) != 'P' || 1663646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration.charAt(len - 1) != 'D') { 1664646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("duration is not formatted correctly. " + 1665646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "Should be 'P<seconds>S' or 'P<days>D'."); 1666646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1667646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1668646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1669646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null && dtend == null) { 1670646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 1671646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "an event."); 1672646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1673646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1674646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1675646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1676646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 16779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 16789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected Uri insertInTransaction(Uri uri, ContentValues values) { 1679ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 16809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 16819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 16829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 16849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 16859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 16879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 16889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 16899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 16909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 16919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 16929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 16939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 16947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 16957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 16967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 16979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 16989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 16999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 1701e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 1702e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff validateEventData(updatedValues); 1703e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 1704e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 17059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 17069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 17079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 17089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 17109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 17119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 17129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 17139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 17149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 17159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 17169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 17179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 17189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 17199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1721646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, updatedValues)) { 172252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 172352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 172452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 172552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 1726646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 17279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 17289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 17299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 17309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, true /* new event */, mDb); 17319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 17339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 17349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 17359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 17369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 17379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 17389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 17409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17418ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // if the Event Timezone is defined, store it as the original one in the 17428ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // ExtendedProperties table 17438ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) { 17448ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE); 17458ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 17468ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues expropsValues = new ContentValues(); 17478ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.EVENT_ID, id); 17488ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.NAME, 17498ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio EXT_PROP_ORIGINAL_TIMEZONE); 17508ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio expropsValues.put(Calendar.ExtendedProperties.VALUE, originalTimezone); 17518ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 17528ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Insert the extended property 17538ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues); 17548ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (exPropId == -1) { 17558ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 17568ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot add the original Timezone in the " 17578ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + "ExtendedProperties table for Event: " + id); 17588ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 17598ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } else { 17608ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Update the Event for saying it has some extended properties 17618ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues eventValues = new ContentValues(); 17628ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1"); 17638ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio int result = mDb.update("Events", eventValues, "_id=?", 17648ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio new String[] {String.valueOf(id)}); 17658ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (result <= 0) { 17668ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 17678ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot update hasExtendedProperties column" 17688ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + " for Event: " + id); 17698ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 17708ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 17718ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 17728ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 17739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff triggerAppWidgetUpdate(id); 17749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 17769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 17779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 17789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 17799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountName = values.getAsString(Calendars._SYNC_ACCOUNT); 17809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 17819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars._SYNC_ACCOUNT_TYPE); 17829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 17839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = values.getAsString(Calendars.URL); 17849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, false /* two-way sync */, calendarUrl); 17859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 17879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 17889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 17899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 17909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 17919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 17929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 17939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.attendeesInsert(values); 17947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 17957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Attendees.EVENT_ID)); 17967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 17979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 17989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 17999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 18009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 18019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 18029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 18039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 18049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 18059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.remindersInsert(values); 18077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 18087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Reminders.EVENT_ID)); 18097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 18109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 18129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 18139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 18149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 18169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 18179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 18189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 18199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 18209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 18219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 18232fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 18242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 18259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 18269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 18279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Calendar.ExtendedProperties.EVENT_ID)) { 18289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 18299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 18309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.extendedPropertiesInsert(values); 18327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 18337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff setEventDirty(values.getAsInteger(Calendar.ExtendedProperties.EVENT_ID)); 18347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 18359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 18369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 18379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 18389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 18399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 18409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 18419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 18429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 18436db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 184468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio case PROVIDER_PROPERTIES: 18457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 18469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 18479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 18489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 18519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 18529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 18549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 18559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 18569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1857e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 1858e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Do some validation on event data before inserting. 1859e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * In particular make sure dtend, duration, etc make sense for 1860e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * the type of event (regular, recurrence, exception). Remove 1861e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * any unexpected fields. 1862e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 1863e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues to insert 1864e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 1865e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private void validateEventData(ContentValues values) { 1866e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 1867e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 1868e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 1869e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 1870e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT)); 1871e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 1872e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 1873e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 1874e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 1875e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 1876e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 1877e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is the recurrence rule 1878e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 1879e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 1880e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 1881e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 1882e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 1883e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence: " + values); 1884e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1885e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 1886e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_EVENT); 1887e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 1888e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1889e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 1890e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 1891e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 1892e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 1893e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 1894e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 1895e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 1896e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 1897e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 1898e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 1899e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 1900e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence exception: " + values); 1901e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1902e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 1903e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1904e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 1905e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 1906e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 1907e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 1908e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 1909e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 1910e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 1911e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 1912e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 1913e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 1914e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 1915e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for event: " + values); 1916e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1917e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 1918e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1919e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1920e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 1921e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 19227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private void setEventDirty(int eventId) { 1923636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.execSQL("UPDATE Events SET _sync_dirty=1 where _id=?", new Integer[] {eventId}); 19247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 19257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 19269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 19279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 19289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 19299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 19309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 19319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 1932f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 193352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 193452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 193552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 1936f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 1937f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 19389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 19399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 19409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 19419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 19429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 19439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 19449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 19459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 19469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 19479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 194852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 194952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 195052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 19519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 19529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 19549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 19559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 19569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 19579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 19609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 19639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 19649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 19659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 19669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 19679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 19689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 19699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 19709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 19719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 19729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 19739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 19749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 19759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 19769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 19779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 19789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 19799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 19809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 19819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 19839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 19849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 19859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 19889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 19899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 19909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 19919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 19929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 19939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 19949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 19959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 19969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 19979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 19989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 20009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 20019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 20029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 20039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 20049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 20059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 20069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 20079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 20089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 20099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 201052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 201152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 201252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 20139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 20149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 20169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 20179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 20189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 20199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 20239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 20249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 20259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 20269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 20279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 20289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 20299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 20309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 20319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 203252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 203352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 203452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 20359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 20369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 20389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 20399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 20419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 20459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 20499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 20509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 20519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 20529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 20559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 20579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 20589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 20599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 20639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 20649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 20659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 20669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 20679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 20719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 20729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 20759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2076636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.update("Events", values, "_id=?", new String[] {String.valueOf(eventId)}); 20779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the instances table when an event is added or updated. 20819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 20829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 20839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param newEvent true if the event is new. 20849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 20859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 20869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateInstancesLocked(ContentValues values, 20879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 20889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean newEvent, 20899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 20909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If there are no expanded Instances, then return. 20929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 20939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (fields.maxInstance == 0) { 20949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 20959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 20989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis == null) { 20999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (newEvent) { 21009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // must be present for a new event. 21019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART missing."); 21029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 210352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 210452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.v(TAG, "Missing DTSTART. No need to update instance."); 210552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 21069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 21109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!newEvent) { 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Want to do this for regular event, recurrence, or exception. 21149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // For recurrence or exception, more deletion may happen below if we 21159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // do an instance expansion. This deletion will suffice if the exception 21169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // is moved outside the window, for instance. 2117636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff db.delete("Instances", "event_id=?", new String[] {String.valueOf(rowId)}); 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (isRecurrenceEvent(values)) { 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The recurrence or exception needs to be (re-)expanded if: 21229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // a) Exception or recurrence that falls inside window 21239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean insideWindow = dtstartMillis <= fields.maxInstance && 21249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff (lastDateMillis == null || lastDateMillis >= fields.minInstance); 21259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // b) Exception that affects instance inside window 21269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // These conditions match the query in getEntries 21279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // See getEntries comment for explanation of subtracting 1 week. 21289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean affectsWindow = originalInstanceTime != null && 21299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime <= fields.maxInstance && 21309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff originalInstanceTime >= fields.minInstance - MAX_ASSUMED_DURATION; 21319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (insideWindow || affectsWindow) { 21329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateRecurrenceInstancesLocked(values, rowId, db); 21339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: an exception creation or update could be optimized by 21359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // updating just the affected instances, instead of regenerating 21369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the recurrence. 21379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 21389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis == null) { 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtendMillis = dtstartMillis; 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if the event is in the expanded range, insert 21469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // into the instances table. 21479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: deal with durations. currently, durations are only used in 21489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrences. 21499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis <= fields.maxInstance && dtendMillis >= fields.minInstance) { 21519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues instanceValues = new ContentValues(); 21529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.EVENT_ID, rowId); 21539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.BEGIN, dtstartMillis); 21549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff instanceValues.put(Instances.END, dtendMillis); 21559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 21579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 21589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 21599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 21609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the timezone-dependent fields. 21639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time local = new Time(); 21649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay) { 21659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = Time.TIMEZONE_UTC; 21669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 21679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff local.timezone = fields.timezone; 21689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff computeTimezoneDependentFields(dtstartMillis, dtendMillis, local, instanceValues); 21719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.instancesInsert(instanceValues); 21729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 21769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Determines the recurrence entries associated with a particular recurrence. 21779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This set is the base recurrence and any exception. 21789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 21799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Normally the entries are indicated by the sync id of the base recurrence 21809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * (which is the originalEvent in the exceptions). 21819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * However, a complication is that a recurrence may not yet have a sync id. 21829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * In that case, the recurrence is specified by the rowId. 21839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 21849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param recurrenceSyncId The sync id of the base recurrence, or null. 21859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The row id of the base recurrence. 21869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return the relevant entries. 21879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 21889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor getRelevantRecurrenceEntries(String recurrenceSyncId, long rowId) { 21899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 21909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 21929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 2193636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String selectionArgs[]; 21949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 2195636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_id =?"; 21969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2197636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {String.valueOf(rowId)}; 21989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 2199636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String where = "_sync_id = ? OR originalEvent = ?"; 22009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.appendWhere(where); 2201636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = new String[] {recurrenceSyncId, recurrenceSyncId}; 22029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.VERBOSE)) { 22049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Retrieving events to expand: " + qb.toString()); 22059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2207636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return qb.query(mDb, EXPAND_COLUMNS, null /* selection */, selectionArgs, 22087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* groupBy */, null /* having */, null /* sortOrder */); 22099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Do incremental Instances update of a recurrence or recurrence exception. 22139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 22149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method does performInstanceExpansion on just the modified recurrence, 22159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid the overhead of recomputing the entire instance table. 22169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 22179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param values The new values of the event. 22189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rowId The database row id of the event. 22199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db The database 22209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateRecurrenceInstancesLocked(ContentValues values, 22229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long rowId, 22239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteDatabase db) { 22249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 222568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 22269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String originalEvent = values.getAsString(Events.ORIGINAL_EVENT); 222768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String recurrenceSyncId; 22289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalEvent != null) { 22299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = originalEvent; 22309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 22319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the recurrence's sync id from the database 22329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff recurrenceSyncId = DatabaseUtils.stringForQuery(db, "SELECT _sync_id FROM Events" 2233636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff + " WHERE _id=?", new String[] {String.valueOf(rowId)}); 22349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // recurrenceSyncId is the _sync_id of the underlying recurrence 22369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the recurrence hasn't gone to the server, it will be null. 22379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to clear out old instances 22399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (recurrenceSyncId == null) { 22409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating updating a recurrence that hasn't gone to the server. 22419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Need to delete based on row id 22429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 22439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 22449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 22459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._id =?)"; 22469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{"" + rowId}); 22479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 22489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Creating or modifying a recurrence or exception. 22499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Delete instances for recurrence (_sync_id = recurrenceSyncId) 22509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // and all exceptions (originalEvent = recurrenceSyncId) 22519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String where = "_id IN (SELECT Instances._id as _id" 22529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 22539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 22549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE Events._sync_id =?" 22559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " OR Events.originalEvent =?)"; 22569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete("Instances", where, new String[]{recurrenceSyncId, recurrenceSyncId}); 22579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Now do instance expansion 22609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor entries = getRelevantRecurrenceEntries(recurrenceSyncId, rowId); 22619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 226268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio performInstanceExpansion(fields.minInstance, fields.maxInstance, instancesTimezone, 22639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries); 22649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (entries != null) { 22669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff entries.close(); 22679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 22749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 22759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 22769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 22779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 22789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 22799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 22809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 22819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 22829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 22839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 22879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 22889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 22959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 22999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2305f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2306f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2307f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2308f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 230952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 231052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 231152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio values.get(Calendar.Events.RRULE), e); 231252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 2313f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2314f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 23159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2316f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 23189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 23199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 23219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 23239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 23249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 23279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 23339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 23419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 23429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 23449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2346e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2347e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 2348e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues (in/out) 2349e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2350e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2351e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 23529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 23539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 23549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 23559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 23569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 23599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 23609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 236152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 236252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 236352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 23649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 23659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 23699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 23709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("event_id", eventId); 23729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 23749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 23769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 23789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 23799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 23829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 23839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 23919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtstart2445", time.format2445()); 23929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 23959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 23969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 23979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("dtend2445", time.format2445()); 23989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 24019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 24029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 24039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 24049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 24059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 24069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 24079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 24089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 24109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("originalInstanceTime2445", time.format2445()); 24119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 24149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 24169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 24179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff rawValues.put("lastDate2445", time.format2445()); 24189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 24219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 24249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) { 2425ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 24269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 24279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 24299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 24309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 24319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 24329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 24339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 24349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 2436dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 24379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 24389323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2439dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2440dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2441dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2442dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 24439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24441ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 24459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 24467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 24471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection = appendAccountToSelection(uri, selection); 24487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 24491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 24501ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", ID_ONLY_PROJECTION, 24511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 24527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 24541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 24551ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 245610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 24579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 245810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 245910b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio triggerAppWidgetUpdate(-1 /* changedEventId */); 24609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 24619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 24629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 24639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 24659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24661ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 24671ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 24681ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 24691ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 24701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 24711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 24721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 24731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 247410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 24751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 24769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 24779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 24787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 24797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Attendees", selection, selectionArgs); 24807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 24817e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Attendees", uri, selection, selectionArgs); 24827e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 24839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 24862fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 24872fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 24882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 24897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 24907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2491636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Attendees", "_id=?", new String[] {String.valueOf(id)}); 24927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 24932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Attendees", uri, null /* selection */, 24942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 24957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 24969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 24989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 24997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 25007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("Reminders", selection, selectionArgs); 25017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 25027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("Reminders", uri, selection, selectionArgs); 25037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 25049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 25072fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 25082fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 25092fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 25117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2512636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("Reminders", "_id=?", new String[] {String.valueOf(id)}); 25137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 25142fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("Reminders", uri, null /* selection */, 25152fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 25162fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25172fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25182fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 25192fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 25202fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 25212fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return mDb.delete("ExtendedProperties", selection, selectionArgs); 25222fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 25232fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, selection, selectionArgs); 25242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 25272fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 25282fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 25292fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 25302fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25312fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 25322fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2533636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("ExtendedProperties", "_id=?", 2534636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 25352fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 25362fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return deleteFromTable("ExtendedProperties", uri, null /* selection */, 25372fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 25387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 25427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 25437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return mDb.delete("CalendarAlerts", selection, selectionArgs); 25447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 25457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteFromTable("CalendarAlerts", uri, selection, selectionArgs); 25467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 25479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 25502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 25512fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 25522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 25532fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 25542fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 25559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2556636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.delete("CalendarAlerts", "_id=?", new String[] {String.valueOf(id)}); 25579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case DELETED_EVENTS: 25597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot delete that URL: " + uri); 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder("_id="); 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 25649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 25659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 25669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 25679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 25699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 25709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2571595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 25727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return deleteMatchingCalendars(selection); // TODO: handle in sync adapter 25739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 25749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 25756db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 257668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio case PROVIDER_PROPERTIES: 25779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 25789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 25799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 25809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 258310b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 25841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2585192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 25861ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 25871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 25881ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff Cursor cursor = mDb.query("Events", EVENTS_PROJECTION, 2589192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank "_id=?", selectionArgs, 2590636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 25911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 25921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 25931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 25941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 25951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 259648f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 259748f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (!emptySyncId) { 25981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 25991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // TODO: we may also want to delete exception 26001ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // events for this event (in case this was a 26011ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // recurring event). We can do that with the 26021ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // following code: 26031ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // mDb.delete("Events", "originalEvent=?", new String[] {syncId}); 26041ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26051ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 26061ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 26071ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 26081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 26091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 26101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 26111ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String origEvent = cursor.getString(EVENTS_ORIGINAL_EVENT_INDEX); 26121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate) 26131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff || !TextUtils.isEmpty(origEvent)) { 26141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 26151ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26161ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 261748f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 261848f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 261948f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 2620192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.delete("Events", "_id=?", selectionArgs); 2621192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.delete("Attendees", "event_id=?", selectionArgs); 26221ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 26231ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 26241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events.DELETED, 1); 26251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 2626192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank mDb.update("Events", values, "_id=?", selectionArgs); 26271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26291ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 26301ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 26311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 26321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26338f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 263410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 263510b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio scheduleNextAlarm(false /* do not remove alarms */); 263610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio triggerAppWidgetUpdate(-1 /* changedEventId */); 263710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 26381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 2639192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank // Delete associated data; attendees, however, are deleted with the actual event so 2640192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank // that the sync adapter is able to notify attendees of the cancellation. 2641636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("Instances", "event_id=?", selectionArgs); 2642636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("EventsRawTimes", "event_id=?", selectionArgs); 2643636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("Reminders", "event_id=?", selectionArgs); 2644636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("CalendarAlerts", "event_id=?", selectionArgs); 2645636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete("ExtendedProperties", "event_id=?", selectionArgs); 26461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 26471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 26481ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 26497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 26507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 26517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 26527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 26537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 26547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 26557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 26567e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 26577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 26587e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 26597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 26607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues values = new ContentValues(); 26617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, "1"); 26627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 26637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 26647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 26657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 26667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2667636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.delete(table, "_id=?", new String[] {String.valueOf(id)}); 2668636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", values, "_id=?", new String[] {String.valueOf(event_id)}); 26697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 26707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 26727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 26737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 26757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 26777e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 26787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 26797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 26807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 26817e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 26827e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 26837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 26847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 26857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 26867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 26877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 26887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 26897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 26907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff ContentValues dirtyValues = new ContentValues(); 26917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff dirtyValues.put(Events._SYNC_DIRTY, "1"); 26927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 26937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 26947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 26957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = c.getLong(ID_INDEX); 26967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long event_id = c.getLong(EVENT_ID_INDEX); 2697636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update(table, values, "_id=?", new String[] {String.valueOf(id)}); 2698636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff mDb.update("Events", dirtyValues, "_id=?", new String[] {String.valueOf(event_id)}); 26997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 27007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 27027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 27037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 27057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 27067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private int deleteMatchingCalendars(String where) { 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 27099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 27119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Cursor c = mDb.query("Calendars", sCalendarsIdProjection, where, 27137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* selectionArgs */, null /* groupBy */, 27147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 27159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27262cc859cab85391a240b9c3f28c935d919c8ceb8cKen Shirriff return mDb.delete("Calendars", where, null /* whereArgs */); 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: call calculateLastDate()! 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 27319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] selectionArgs) { 2733ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final boolean callerIsSyncAdapter = 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff readBooleanQueryParameter(uri, Calendar.CALLER_IS_SYNCADAPTER, false); 27439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 274568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS 274668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio && match != EVENTS && match != PROVIDER_PROPERTIES) { 27479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException( 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff "WHERE based updates not supported"); 27499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 27519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 27539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 27569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 2757dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String selectionWithId = (BaseColumns._ID + "=?") 2758dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 27599323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2760dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2761dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2762dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 27669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27672fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 27682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 27692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 27719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 27729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 27739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 27749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2776636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Calendars", values, "_id=?", 2777636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 27789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2779616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa // The calendar should not be displayed in widget either. 2780616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa final Integer selected = values.getAsInteger(Calendars.SELECTED); 2781616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa if (selected != null && selected == 0) { 2782616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa triggerAppWidgetUpdate(-1); 2783616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa } 2784616e6f19fcef87a7dcc910a95c4c340683648ad0Daisuke Miyakawa 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 27869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 27889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 27899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = 0; 27917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (match == EVENTS_ID) { 27927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = ContentUris.parseId(uri); 2793a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else if (callerIsSyncAdapter) { 2794a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff if (selection != null && selection.startsWith("_id=")) { 27957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // The ContentProviderOperation generates an _id=n string instead of 27967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // adding the id to the URL, so parse that out here. 27977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = Long.parseLong(selection.substring(4)); 2798a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else { 2799a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // Sync adapter Events operation affects just Events table, not associated 2800a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // tables. 2801646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, values)) { 280252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 280352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "updateInTransaction: Caller is sync adapter. " + 280452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 280552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 2806646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2807a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff return mDb.update("Events", values, selection, selectionArgs); 2808a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } 28097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2810a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 28117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 28137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff values.put(Events._SYNC_DIRTY, 1); 28147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Disallow updating the attendee status in the Events 28169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // table. In the future, we could support this but we 28179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // would have to query and update the attendees table 28189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to keep the values consistent. 28199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 28209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 28219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.SELF_ATTENDEE_STATUS 28229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // TODO: should we allow this? 28267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (values.containsKey(Events.HTML_URI) && !callerIsSyncAdapter) { 28279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 28289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.HTML_URI 28299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 28309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2831e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 2832e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // TODO: should extend validateEventData to work with updates and call it here 2833e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 28349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 283552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 283652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Could not update event."); 283752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2840646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we pass in a uri with the id appended to fixAllDayTime 2841646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Uri allDayUri; 2842646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 1) { 2843646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = ContentUris.withAppendedId(uri, id); 2844646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 2845646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = uri; 2846646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2847646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(allDayUri, updatedValues)) { 284852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 284952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "updateInTransaction: " + 285052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 285152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 2852646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 28539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2854636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int result = mDb.update("Events", updatedValues, "_id=?", 2855636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 28569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (result > 0) { 28579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 28589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateInstancesLocked(updatedValues, id, false /* not a new event */, mDb); 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTSTART)) { 28619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The start time of the event changed, so run the 28629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarm scheduler. 28639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 28649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing event"); 28659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 28679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff triggerAppWidgetUpdate(id); 28689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 28719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28722fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 28732fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28742fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28752fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 28779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 28789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2881636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("Attendees", values, "_id=?", 288283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 28837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 28842fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("Attendees", values, uri, null /* selection */, 28852fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 28892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 28932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 28949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2895636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("CalendarAlerts", values, "_id=?", 2896636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 28979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28982fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 28992fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 29002fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDb.update("CalendarAlerts", values, selection, selectionArgs); 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29032fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 29042fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 29052fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 29062fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 29077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 29087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2909636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff count = mDb.update("Reminders", values, "_id=?", 291083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 29117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 29122fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff count = updateInTable("Reminders", values, uri, null /* selection */, 29132fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 29147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 29169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 29179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 29189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 29199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 29209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarm(false /* do not remove alarms */); 29227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 29239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 29252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 29262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 29272fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 29287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 29297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2930636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return mDb.update("ExtendedProperties", values, "_id=?", 2931636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 29327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 29332fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff return updateInTable("ExtendedProperties", values, uri, null /* selection */, 29342fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 29357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 29369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 293783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 293883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 293983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 294083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(false); 294183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 294283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 294383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 294483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff scheduleNextAlarm(true); 294583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 294683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 29479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 294868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio case PROVIDER_PROPERTIES: { 294968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (selection == null) { 295068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio throw new UnsupportedOperationException("Selection cannot be null for " + uri); 295168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 295268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (!selection.equals("key=?")) { 295368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 295468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 295568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 295668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 295768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 295868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 295968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 296068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 296168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 296268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 296368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 296468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 296568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 296668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Update the database with the provided values (this call may change the value 296768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // of timezone Instances) 296868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio int result = mDb.update("CalendarCache", values, selection, selectionArgs); 296968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 297068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if successful, do some house cleaning: 297168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if the timezone type is set to "home", set the Instances timezone to the previous 297268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if the timezone type is set to "auto", set the Instances timezone to the current 297368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // device one 297468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if the timezone Instances is set AND if we are in "home" timezone type, then 297568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // save the timezone Instance into "previous" too 297668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (result > 0) { 297768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // If we are changing timezone type... 297868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 297968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 298068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (value != null) { 298168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if we are setting timezone type to "home" 298268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 298368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String previousTimezone = 298468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 298568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (previousTimezone != null) { 298668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 298768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 298868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 298968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 299068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio regenerateInstancesTable(); 299168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 299268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 299368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if we are setting timezone type to "auto" 299468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 299568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 299668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 2997830f982e42eafaeb95b72fef9830167d39b025dcErik if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 2998830f982e42eafaeb95b72fef9830167d39b025dcErik regenerateInstancesTable(); 2999830f982e42eafaeb95b72fef9830167d39b025dcErik } 300068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 300168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 300268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 300368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // If we are changing timezone Instances... 300468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 300568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // if we are in "home" timezone type... 300668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (isHomeTimezone()) { 300768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 300868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Update the previous value 300968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 301068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // Recompute Instances if the "home" timezone has changed 301168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 301268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 301368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio regenerateInstancesTable(); 301468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 301568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 301668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 30171431b9ded6f7a429a6f3c9e737fc79320627409cErik triggerAppWidgetUpdate(-1); 301868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 301968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio return result; 302068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio } 302168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 30229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 30239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 30249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3027595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) { 3028595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountName = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_NAME); 3029595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountType = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_TYPE); 3030595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 3031595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere(Calendar.Calendars._SYNC_ACCOUNT + "=" 3032595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3033595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 3034595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 3035595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 3036595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff qb.appendWhere("1"); // I.e. always true 3037595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3038595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3039595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 30409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 3041595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountName = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_NAME); 3042595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String accountType = getQueryParameter(uri, Calendar.EventsEntity.ACCOUNT_TYPE); 30439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 30449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff StringBuilder selectionSb = new StringBuilder(Calendar.Calendars._SYNC_ACCOUNT + "=" 30459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountName) + " AND " 30469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=" 30479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 30489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 30499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 30509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 30519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 30529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selectionSb.toString(); 30549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 30559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 30569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 30609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 30619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 30629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 30639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] {Calendars._SYNC_ACCOUNT, Calendars._SYNC_ACCOUNT_TYPE, 30649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Calendars.URL, Calendars.SYNC_EVENTS}, 30659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 30669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 30679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 30689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 30709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 3072ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 30739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3074ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 3075ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 3076ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 3077ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 3078ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 3079ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 3080ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 30819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 30829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 30839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30869535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 30879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 308852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 308952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 309052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "is empty -- should not happen."); 309152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 30929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 30939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30959535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 30969535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 30979535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 30989535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 30999535627bf6295cd94447beb83e1aac41f50c3600Erik } 31009535627bf6295cd94447beb83e1aac41f50c3600Erik 31019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 31029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 31039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 31049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 31079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 31089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 31099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this needed 31129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// @Override 31139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// public void onSyncStop(SyncContext context, boolean success) { 31149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// super.onSyncStop(context, success); 31159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// if (Log.isLoggable(TAG, Log.DEBUG)) { 31169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// Log.d(TAG, "onSyncStop() success: " + success); 31179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 31189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// scheduleNextAlarm(false /* do not remove alarms */); 31199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// triggerAppWidgetUpdate(-1); 31209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff// } 31219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 31239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Update any existing widgets with the changed events. 31249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 31259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param changedEventId Specific event known to be changed, otherwise -1. 31269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * If present, we use it to decide if an update is necessary. 31279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 31289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private synchronized void triggerAppWidgetUpdate(long changedEventId) { 31299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 31309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (context != null) { 31319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mAppWidgetProvider.providerUpdated(context, changedEventId); 31329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Retrieve and cache the alarm manager */ 31369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private AlarmManager getAlarmManager() { 31379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff synchronized(mAlarmLock) { 31389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (mAlarmManager == null) { 31399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 31409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (context == null) { 314152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 314252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "getAlarmManager() cannot get Context"); 314352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 31449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 31459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Object service = context.getSystemService(Context.ALARM_SERVICE); 31479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mAlarmManager = (AlarmManager) service; 31489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mAlarmManager; 31509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarmCheck(long triggerTime) { 31549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager manager = getAlarmManager(); 31559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (manager == null) { 315652913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 315752913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "scheduleNextAlarmCheck() cannot get AlarmManager"); 315852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 31599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 31609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Context context = getContext(); 31629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Intent intent = new Intent(CalendarReceiver.SCHEDULE); 31639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff intent.setClass(context, CalendarReceiver.class); 31649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff PendingIntent pending = PendingIntent.getBroadcast(context, 31659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_NO_CREATE); 31669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (pending != null) { 31679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Cancel any previous alarms that do the same thing. 31689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.cancel(pending); 31699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff pending = PendingIntent.getBroadcast(context, 31719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 31729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 31749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 31759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(triggerTime); 31769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 31779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "scheduleNextAlarmCheck at: " + triggerTime + timeStr); 31789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff manager.set(AlarmManager.RTC_WAKEUP, triggerTime, pending); 31819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 31849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs the alarm scheduler in a background thread. 31859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 31869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff void scheduleNextAlarm(boolean removeAlarms) { 31879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new AlarmScheduler(removeAlarms); 31889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 31899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 31929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread and schedules an alarm for 31939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the next calendar event, if necessary. 31949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 31959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void runScheduleNextAlarm(boolean removeAlarms) { 31969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getWritableDatabase(); 31979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.beginTransaction(); 31989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 31999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (removeAlarms) { 32009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff removeScheduledAlarmsLocked(db); 32019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff scheduleNextAlarmLocked(db); 32039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.setTransactionSuccessful(); 32049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 32059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.endTransaction(); 32069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 32109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method looks at the 24-hour window from now for any events that it 32119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * needs to schedule. This method runs within a database transaction. It 32129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * also runs in a background thread. 32139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 32149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The CalendarProvider2 keeps track of which alarms it has already scheduled 32159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * to avoid scheduling them more than once and for debugging problems with 32169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms. It stores this knowledge in a database table called CalendarAlerts 32179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * which persists across reboots. But the actual alarm list is in memory 32189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and disappears if the phone loses power. To avoid missing an alarm, we 32199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * clear the entries in the CalendarAlerts table when we start up the 32209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * CalendarProvider2. 32219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 32229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Scheduling an alarm multiple times is not tragic -- we filter out the 32239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * extra ones when we receive them. But we still need to keep track of the 32249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled alarms. The main reason is that we need to prevent multiple 32259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * notifications for the same alarm (on the receive side) in case we 32269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * accidentally schedule the same alarm multiple times. We don't have 32279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * visibility into the system's alarm list so we can never know for sure if 32289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we have already scheduled an alarm and it's better to err on scheduling 32299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * an alarm twice rather than missing an alarm. Another reason we keep 32309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * track of scheduled alarms in a database table is that it makes it easy to 32319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * run an SQL query to find the next reminder that we haven't scheduled. 32329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 32339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 32349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 32359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void scheduleNextAlarmLocked(SQLiteDatabase db) { 32369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AlarmManager alarmManager = getAlarmManager(); 32379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmManager == null) { 323852913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 323952913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "Failed to find the AlarmManager. Could not schedule the next alarm!"); 324052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 32419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 32429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long currentMillis = System.currentTimeMillis(); 32459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long start = currentMillis - SCHEDULE_ALARM_SLACK; 32469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final long end = start + (24 * 60 * 60 * 1000); 32479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentResolver cr = getContext().getContentResolver(); 32489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 32499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 32509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(start); 32519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 32529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "runScheduleNextAlarm() start search: " + startTimeStr); 32539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32558f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Delete rows in CalendarAlert where the corresponding Instance or 32568f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Reminder no longer exist. 32578f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Also clear old alarms but keep alarms around for a while to prevent 32589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // multiple alerts for the same reminder. The "clearUpToTime' 32599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should be further in the past than the point in time where 32609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // we start searching for events (the "start" variable defined above). 32618f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff String selectArg[] = new String[] { 32628f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Long.toString(currentMillis - CLEAR_OLD_ALARM_THRESHOLD) 32638f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff }; 32648f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 32658f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff int rowsDeleted = 32668f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff db.delete(CalendarAlerts.TABLE_NAME, INVALID_CALENDARALERTS_SELECTOR, selectArg); 32679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long nextAlarmTime = end; 32698f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long tmpAlarmTime = CalendarAlerts.findNextAlarmTime(cr, currentMillis); 32708f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (tmpAlarmTime != -1 && tmpAlarmTime < nextAlarmTime) { 32718f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff nextAlarmTime = tmpAlarmTime; 32729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Extract events from the database sorted by alarm time. The 32759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // alarm times are computed from Instances.begin (whose units 32769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // are milliseconds) and Reminders.minutes (whose units are 32779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // minutes). 32789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 32799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events whose end time is already in the past. 32809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Also, ignore events alarms that we have already scheduled. 32819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 32829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 1: we can add support for the case where Reminders.minutes 32839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // equals -1 to mean use Calendars.minutes by adding a UNION for 32849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that case where the two halves restrict the WHERE clause on 32859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders.minutes != -1 and Reminders.minutes = 1, respectively. 32869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 32879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note 2: we have to name "myAlarmTime" different from the 32889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "alarmTime" column in CalendarAlerts because otherwise the 32899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query won't find multiple alarms for the same event. 3290156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // 3291156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // The CAST is needed in the query because otherwise the expression 3292156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will be untyped and sqlite3's manifest typing will not convert the 3293156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // string query parameter to an int in myAlarmtime>=?, so the comparison 3294156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff // will fail. This could be simplified if bug 2464440 is resolved. 32959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String query = "SELECT begin-(minutes*60000) AS myAlarmTime," 32969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " Instances.event_id AS eventId, begin, end," 32979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " title, allDay, method, minutes" 32989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " FROM Instances INNER JOIN Events" 32999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Events._id = Instances.event_id)" 33009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " INNER JOIN Reminders" 33019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ON (Instances.event_id = Reminders.event_id)" 33029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE method=" + Reminders.METHOD_ALERT 3303156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime>=CAST(? AS INT)" 3304156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND myAlarmTime<=CAST(? AS INT)" 3305156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff + " AND end>=?" 33069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " AND 0=(SELECT count(*) from CalendarAlerts CA" 33079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " where CA.event_id=Instances.event_id AND CA.begin=Instances.begin" 33089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " AND CA.alarmTime=myAlarmTime)" 33099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " ORDER BY myAlarmTime,begin,title"; 3310156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff String queryParams[] = new String[] {String.valueOf(start), String.valueOf(nextAlarmTime), 3311156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff String.valueOf(currentMillis)}; 33129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 331368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 331468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio boolean isHomeTimezone = mCalendarCache.readTimezoneType().equals( 331568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio CalendarCache.TIMEZONE_TYPE_HOME); 3316d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio acquireInstanceRangeLocked(start, 3317d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio end, 3318d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* don't use minimum expansion windows */, 331968040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio false /* do not force Instances deletion and expansion */, 332068040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio instancesTimezone, 332168040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio isHomeTimezone); 33229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 33239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3324156ad29fe71eaae73cddad9b17690d1cc8225136Ken Shirriff cursor = db.rawQuery(query, queryParams); 33259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33268f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int beginIndex = cursor.getColumnIndex(Instances.BEGIN); 33278f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int endIndex = cursor.getColumnIndex(Instances.END); 33288f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int eventIdIndex = cursor.getColumnIndex("eventId"); 33298f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int alarmTimeIndex = cursor.getColumnIndex("myAlarmTime"); 33308f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutesIndex = cursor.getColumnIndex(Reminders.MINUTES); 33319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 33339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 33349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(nextAlarmTime); 33359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String alarmTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 33368f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "cursor results: " + cursor.getCount() + " nextAlarmTime: " 33378f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + alarmTimeStr); 33389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (cursor.moveToNext()) { 33419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule all alarms whose alarm time is as early as any 33429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // scheduled alarm. For example, if the earliest alarm is at 33439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // 1pm, then we will schedule all alarms that occur at 1pm 33449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // but no alarms that occur later than 1pm. 33459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Actually, we allow alarms up to a minute later to also 33469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // be scheduled so that we don't have to check immediately 33479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // again after an event alarm goes off. 33488f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long alarmTime = cursor.getLong(alarmTimeIndex); 33498f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long eventId = cursor.getLong(eventIdIndex); 33508f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final int minutes = cursor.getInt(minutesIndex); 33518f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long startTime = cursor.getLong(beginIndex); 33528f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff final long endTime = cursor.getLong(endIndex); 33539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 33559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(); 33569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(alarmTime); 33579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); 33589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(startTime); 33599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String startTimeStr = time.format(" %a, %b %d, %Y %I:%M%P"); 33608f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 33618f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, " looking at id: " + eventId + " " + startTime + startTimeStr 33628f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff + " alarm: " + alarmTime + schedTime); 33639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (alarmTime < nextAlarmTime) { 33669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff nextAlarmTime = alarmTime; 33679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (alarmTime > 33681edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff nextAlarmTime + DateUtils.MINUTE_IN_MILLIS) { 33699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This event alarm (and all later ones) will be scheduled 33709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // later. 33718f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 33728f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff Log.d(TAG, "This event alarm (and all later ones) will be scheduled later"); 33738f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 33749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 33759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Avoid an SQLiteContraintException by checking if this alarm 33789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // already exists in the table. 33799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (CalendarAlerts.alarmExists(cr, eventId, startTime, alarmTime)) { 33809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 33819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int titleIndex = cursor.getColumnIndex(Events.TITLE); 33829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String title = cursor.getString(titleIndex); 33839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, " alarm exists for id: " + eventId + " " + title); 33849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 33869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Insert this alarm into the CalendarAlerts table 33899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Uri uri = CalendarAlerts.insert(cr, eventId, startTime, 33909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endTime, alarmTime, minutes); 33919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (uri == null) { 339252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 339352913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.e(TAG, "runScheduleNextAlarm() insert into " 339452913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio + "CalendarAlerts table failed"); 339552913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 33969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff continue; 33979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33998f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, alarmTime); 34009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 34029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 34039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 34049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34078f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff // Refresh notification bar 34088f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff if (rowsDeleted > 0) { 34098f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff CalendarAlerts.scheduleAlarm(getContext(), alarmManager, currentMillis); 34108f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff } 34118f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 34129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we scheduled an event alarm, then schedule the next alarm check 34139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for one minute past that alarm. Otherwise, if there were no 34149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event alarms scheduled, then check again in 24 hours. If a new 34159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // event is inserted before the next alarm check, then this method 34169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will be run again when the new event is inserted. 34179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (nextAlarmTime != Long.MAX_VALUE) { 34181edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(nextAlarmTime + DateUtils.MINUTE_IN_MILLIS); 34199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 34201edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriff scheduleNextAlarmCheck(currentMillis + DateUtils.DAY_IN_MILLIS); 34219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 34259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Removes the entries in the CalendarAlerts table for alarms that we have 34269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * scheduled but that have not fired yet. We do this to ensure that we 34279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * don't miss an alarm. The CalendarAlerts table keeps track of the 34289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * alarms that we have scheduled but the actual alarm list is in memory 34299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and will be cleared if the phone reboots. 34309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 34319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We don't need to remove entries that have already fired, and in fact 34329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * we should not remove them because we need to display the notifications 34339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * until the user dismisses them. 34349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 34359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * We could remove entries that have fired and been dismissed, but we leave 34369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * them around for a while because it makes it easier to debug problems. 34379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Entries that are old enough will be cleaned up later when we schedule 34389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * new alarms. 34399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 34409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void removeScheduledAlarmsLocked(SQLiteDatabase db) { 34419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 34429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "removing scheduled alarms"); 34439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff db.delete(CalendarAlerts.TABLE_NAME, 34459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff CalendarAlerts.STATE + "=" + CalendarAlerts.SCHEDULED, null /* whereArgs */); 34469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sEventsTable = "Events"; 34499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sAttendeesTable = "Attendees"; 34509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sRemindersTable = "Reminders"; 34519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sCalendarAlertsTable = "CalendarAlerts"; 34529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static String sExtendedPropertiesTable = "ExtendedProperties"; 34539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 34559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 34569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 34579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int DELETED_EVENTS = 4; 34589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS = 5; 34599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_ID = 6; 34609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES = 7; 34619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int ATTENDEES_ID = 8; 34629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS = 9; 34639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int REMINDERS_ID = 10; 34649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES = 11; 34659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EXTENDED_PROPERTIES_ID = 12; 34669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS = 13; 34679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_ID = 14; 34689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDAR_ALERTS_BY_INSTANCE = 15; 34696db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int INSTANCES_BY_DAY = 16; 34706db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE = 17; 34716db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int SYNCSTATE_ID = 18; 34726db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES = 19; 34736db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_ENTITIES_ID = 20; 34746db535b458146a279bebd4a51d56c1bdfc204528Erik private static final int EVENT_DAYS = 21; 347583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM = 22; 347683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff private static final int SCHEDULE_ALARM_REMOVE = 23; 347748587d3291c4db7f0942e1bff55b88cfa7764ba0Erik private static final int TIME = 24; 347868040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private static final int PROVIDER_PROPERTIES = 25; 34799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 34819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 34829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sEventsProjectionMap; 348319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 34849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 34859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 34869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 348768040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 34889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 3490b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/when/*/*", INSTANCES); 3491b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 3492b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 3493b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events", EVENTS); 3494b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "events/#", EVENTS_ID); 3495b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities", EVENT_ENTITIES); 3496b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 3497b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars", CALENDARS); 3498b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendars/#", CALENDARS_ID); 3499b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "deleted_events", DELETED_EVENTS); 3500b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees", ATTENDEES); 3501b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "attendees/#", ATTENDEES_ID); 3502b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders", REMINDERS); 3503b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "reminders/#", REMINDERS_ID); 3504b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 3505b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "extendedproperties/#", EXTENDED_PROPERTIES_ID); 3506b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 3507b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 3508b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "calendar_alerts/by_instance", 3509b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 3510c4e53191b570e09959c5723f4d253977ba48f2d0Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate", SYNCSTATE); 351183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 351283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_PATH, SCHEDULE_ALARM); 351383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff sUriMatcher.addURI(Calendar.AUTHORITY, SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 351448587d3291c4db7f0942e1bff55b88cfa7764ba0Erik sUriMatcher.addURI(Calendar.AUTHORITY, "time/#", TIME); 3515997e2e5cb006682bc1a82441304994b458d9745dErik sUriMatcher.addURI(Calendar.AUTHORITY, "time", TIME); 351668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio sUriMatcher.addURI(Calendar.AUTHORITY, "properties", PROVIDER_PROPERTIES); 35179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 35199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 35209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HTML_URI, "htmlUri"); 35219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TITLE, "title"); 35229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 35239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DESCRIPTION, "description"); 35249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.STATUS, "eventStatus"); 35259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 35269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 35279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTSTART, "dtstart"); 35289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DTEND, "dtend"); 35299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 35309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.DURATION, "duration"); 35319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ALL_DAY, "allDay"); 35329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.VISIBILITY, "visibility"); 35339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.TRANSPARENCY, "transparency"); 35349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 35359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 35369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RRULE, "rrule"); 35379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.RDATE, "rdate"); 35389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXRULE, "exrule"); 35399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.EXDATE, "exdate"); 35409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 35419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 35429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 35439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.LAST_DATE, "lastDate"); 35449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 35459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 35469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 35479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 35489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 35499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events.ORGANIZER, "organizer"); 35507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff sEventsProjectionMap.put(Events.DELETED, "deleted"); 35519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3552e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 35531ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 35541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 35551ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 35569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 3557982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.COLOR, "color"); 3558982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.ACCESS_LEVEL, "access_level"); 3559982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sEventsProjectionMap.put(Calendars.SELECTED, "selected"); 35609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.URL, "url"); 35619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.TIMEZONE, "timezone"); 35629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, "ownerAccount"); 35639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3564982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 3565e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 3566e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 3567982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3568e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3569982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 35701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._ID, "_id"); 35711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ID, "_sync_id"); 35721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_VERSION, "_sync_version"); 35731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_TIME, "_sync_time"); 3574c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventsProjectionMap.put(Events._SYNC_DATA, "_sync_local_id"); 35751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_DIRTY, "_sync_dirty"); 35761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT, "_sync_account"); 35779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap.put(Events._SYNC_ACCOUNT_TYPE, 35781ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff "_sync_account_type"); 35799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 358046f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 358119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HTML_URI, "htmlUri"); 358219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TITLE, "title"); 358319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DESCRIPTION, "description"); 358419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, "eventLocation"); 358519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.STATUS, "eventStatus"); 358619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, "selfAttendeeStatus"); 358719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.COMMENTS_URI, "commentsUri"); 358819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTSTART, "dtstart"); 358919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DTEND, "dtend"); 359019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DURATION, "duration"); 359119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, "eventTimezone"); 359219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ALL_DAY, "allDay"); 359319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.VISIBILITY, "visibility"); 359419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.TRANSPARENCY, "transparency"); 359519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ALARM, "hasAlarm"); 359619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, "hasExtendedProperties"); 359719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RRULE, "rrule"); 359819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.RDATE, "rdate"); 359919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXRULE, "exrule"); 360019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.EXDATE, "exdate"); 360119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_EVENT, "originalEvent"); 360219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, "originalInstanceTime"); 360319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, "originalAllDay"); 360419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.LAST_DATE, "lastDate"); 360519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, "hasAttendeeData"); 360619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, "calendar_id"); 360719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, "guestsCanInviteOthers"); 360819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, "guestsCanModify"); 360919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, "guestsCanSeeGuests"); 361019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.ORGANIZER, "organizer"); 361119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events.DELETED, "deleted"); 361219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 361319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 3614c12fe4704e12519756b8da1a3f9199f2013e48f0Marc Blank sEventEntitiesProjectionMap.put(Events._SYNC_DATA, Events._SYNC_DATA); 361519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_VERSION, Events._SYNC_VERSION); 361619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_DIRTY, Events._SYNC_DIRTY); 361719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Calendars.URL, "url"); 361819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 36199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 36209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 36219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 36229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 36239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 36249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 36259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 36269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 36279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 36289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 36309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 36319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 36329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 36339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 36349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 36359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 36369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 36379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 36399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 36409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 36419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 36429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 36439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 36459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 36469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 36479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 36489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 36499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 36509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 36519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 365268040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio 365368040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio // CalendarCache columns 365468040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 365568040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 365668040cf2602bb15bb4cf1072cf99132118d5a805Fabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 36579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 36609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make sure that there are no entries for accounts that no longer 36619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * exist. We are overriding this since we need to delete from the 36629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 36637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 36647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 36659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 36669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 36679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb = mDbHelper.getWritableDatabase(); 36689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (mDb == null) return; 36699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 367046f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 367146f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 36729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 36739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 36749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 36759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 36779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 36799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 36809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (String table : new String[]{"Calendars"}) { 36829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Find all the accounts the contacts DB knows about, mark the ones that aren't 36839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 36849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor c = mDb.rawQuery("SELECT DISTINCT " + CalendarDatabaseHelper.ACCOUNT_NAME 36859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "," 36869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + CalendarDatabaseHelper.ACCOUNT_TYPE + " from " 36879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + table, null); 36889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 36899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c.getString(0) != null && c.getString(1) != null) { 36909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 36919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 36929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 36939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 36979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 370052913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 370152913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 370252913fc7cdf91c335e3559e0b38f9b3e2090737fFabrice Di Meglio } 37039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 37049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.execSQL("DELETE FROM Calendars" 37059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " WHERE " + CalendarDatabaseHelper.ACCOUNT_NAME + "= ? AND " 37069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + CalendarDatabaseHelper.ACCOUNT_TYPE 37079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "= ?", params); 37089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 37109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 37119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 37129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 37139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3716595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /* package */ static boolean readBooleanQueryParameter(Uri uri, String name, 3717595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff boolean defaultValue) { 3718595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff final String flag = getQueryParameter(uri, name); 37199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return flag == null 37209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ? defaultValue 37219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase())); 37229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 37239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3724595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff // Duplicated from ContactsProvider2. TODO: a utility class for shared code 3725595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /** 3726595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff * A fast re-implementation of {@link Uri#getQueryParameter} 3727595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff */ 3728595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff /* package */ static String getQueryParameter(Uri uri, String parameter) { 3729595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff String query = uri.getEncodedQuery(); 3730595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (query == null) { 3731595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3732595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3733595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3734595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int queryLength = query.length(); 3735595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int parameterLength = parameter.length(); 3736595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3737595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff String value; 3738595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int index = 0; 3739595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff while (true) { 3740595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index = query.indexOf(parameter, index); 3741595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (index == -1) { 3742595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3743595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3744595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3745595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index += parameterLength; 3746595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3747595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (queryLength == index) { 3748595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return null; 3749595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3750595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3751595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (query.charAt(index) == '=') { 3752595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff index++; 3753595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff break; 3754595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3755595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3756595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3757595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff int ampIndex = query.indexOf('&', index); 3758595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (ampIndex == -1) { 3759595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff value = query.substring(index); 3760595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 3761595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff value = query.substring(index, ampIndex); 3762595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3763595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 3764595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff return Uri.decode(value); 3765595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3766636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff 3767636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 3768636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 3769636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 3770636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 3771636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 3772636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 3773636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 3774636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 3775636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 3776636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 3777636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 3778636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 3779636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 3780636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 3781636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 3782636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 3783636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 3784636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 3785636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 37869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 3787