CalendarProvider2.java revision b09eb917f2490a1dae20709a667df845a2e67c94
19f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/* 29f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 39f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Copyright 2006, The Android Open Source Project 49f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 59f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Licensed under the Apache License, Version 2.0 (the "License"); 69f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** you may not use this file except in compliance with the License. 79f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** You may obtain a copy of the License at 89f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 99f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** http://www.apache.org/licenses/LICENSE-2.0 109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Unless required by applicable law or agreed to in writing, software 129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** distributed under the License is distributed on an "AS IS" BASIS, 139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** See the License for the specific language governing permissions and 149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** limitations under the License. 169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff*/ 179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpackage com.android.providers.calendar; 199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 207be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 217be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 22370f91c0cfe5a5fecaba6120e703f4d2271d2277Erikimport com.google.common.annotations.VisibleForTesting; 23370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.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; 41a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 42a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 44f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglioimport android.pim.EventRecurrence; 459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.pim.RecurrenceSet; 469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 47b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract; 48b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Attendees; 49b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.CalendarAlerts; 50b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Calendars; 51b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Events; 52b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Instances; 53b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Reminders; 54b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.SyncState; 559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 561edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 57192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 60ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 622ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErikimport java.lang.reflect.Array; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 64ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 67dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 68bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFaddenimport java.util.Set; 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 70dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 75b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik * is defined in {@link android.provider.CalendarContract}. 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 808bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 827be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 83c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String ACCOUNT_SELECTION_PREFIX = Calendars.ACCOUNT_NAME + "=? AND " 84c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik + Calendars.ACCOUNT_TYPE + "=?"; 857be45683e367bd6897daf6444b03be938f8f5eaaErik 86f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final boolean PROFILE = false; 879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 888f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 96b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Events.ORIGINAL_ID, 97c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_SYNC_ID, 989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 999ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 1009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 103b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_ID_INDEX = 3; 104b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4; 1057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 114646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 115646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 116646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 117646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 118646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 119646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 120646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 121646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 122646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 123646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 124646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 125646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 126646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 127646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 128646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 129646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 135ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 138f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik private CalendarInstancesHelper mInstancesHelper; 1399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1408ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 141f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // Due to an issue in Calendar Server restricting the length of the name we 142f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // had to strip it down 1438ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1448ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1458ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1468ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1478ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1483443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 149b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + ", " + 150b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTSTART_2445 + ", " + 151b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTEND_2445 + ", " + 1523443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1533443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 154b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 155b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1563443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 157b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 158b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 159b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " + 160b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 161c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik " SET " + Events.DIRTY + "=1" + 162b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 163b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 1642ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik protected static final String SQL_WHERE_ID = Events._ID + "=?"; 165b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EVENT_ID = "event_id=?"; 166ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak private static final String SQL_WHERE_ORIGINAL_EVENT = Events.ORIGINAL_SYNC_ID + "=?"; 167b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 168b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + 169b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID; 170b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 171b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 172b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 173b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID; 174b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 175b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 1762ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 177b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 178b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 179b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 1802ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 181b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 182b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 183b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 184b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 185b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 186b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik Tables.EXTENDED_PROPERTIES + "." + CalendarContract.ExtendedProperties._ID + "=?"; 187b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 188b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 1892ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik " WHERE " + Calendars.ACCOUNT_NAME + "=? AND " + 1902ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + "=?"; 191b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 192fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 193fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 194fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 1959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 1969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 1979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 1989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 1999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 20481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 20581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 20681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 20781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 208b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 20981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 210b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 21181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 21218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 21318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 21418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 21518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 21618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 217b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 21818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 219b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")" + ") LEFT OUTER JOIN " + 22018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 22118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 222b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Attendees.EVENT_ID + "=" + 22318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 224b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 22518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 226b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 227b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.START_DAY + "<=? AND " + 228b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END_DAY + ">=?"; 22981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 230b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 231b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.BEGIN + "<=? AND " + 232b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END + ">=?"; 2339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 2412ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * The sort order is: events with an earlier start time occur first and if 2422ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the start times are the same, then events with a later end time occur 2432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * first. The later end time is ordered first so that long-running events in 2442ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the calendar views appear first. If the start and end times of two events 2452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * are the same then we sort alphabetically on the title. This isn't 2462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * required for correctness, it just adds a nice touch. 2472ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 2482ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; 2492ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 2502ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 2512ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * A regex for describing how we split search queries into tokens. Keeps 2522ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * quoted phrases as one token. "one \"two three\"" ==> ["one" "two three"] 253dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 254dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 255dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 256dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 257dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 258dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 259dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 260dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 261dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 262dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 26381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 264dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 265dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 266dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 267dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 268dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 269dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 270dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 271dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 272dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 27381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 27418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 27518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 27618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 27718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 27818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 279b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_EMAIL + ")"; 28018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 28118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 28218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 28318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 28418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 28518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 286b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_NAME + ")"; 28718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 28881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 289b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.TITLE, 290b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.DESCRIPTION, 291b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.EVENT_LOCATION, 29218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 29318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 29481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 29581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 296a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 297a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 298a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 299a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 300a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 301a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 302a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 303a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 304a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 305a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 306a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 307a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 308dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 309dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 310dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 311dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 312dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 313bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Set of columns allowed to be altered when creating an exception to a recurring event. */ 314bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final HashSet<String> ALLOWED_IN_EXCEPTION = new HashSet<String>(); 315bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden static { 316bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // _id, _sync_account, _sync_account_type, dirty, _sync_mark, calendar_id 317bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events._SYNC_ID); 318bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA1); 319bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA7); 32002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA3); 321bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.TITLE); 322bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION); 323bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION); 324bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.STATUS); 325c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS); 32602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6); 327bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DTSTART); 328c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // dtend -- set from duration as part of creating the exception 329bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_TIMEZONE); 330bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_END_TIMEZONE); 331bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DURATION); 332bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ALL_DAY); 333bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ACCESS_LEVEL); 334bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.AVAILABILITY); 335bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ALARM); 336bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_EXTENDED_PROPERTIES); 337bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RRULE); 338bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RDATE); 339bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXRULE); 340bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXDATE); 341bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_SYNC_ID); 342bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_INSTANCE_TIME); 343bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // originalAllDay, lastDate 344bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ATTENDEE_DATA); 345bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_MODIFY); 346bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_INVITE_OTHERS); 347bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_SEE_GUESTS); 348bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORGANIZER); 349bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // deleted, original_id, alerts 350bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 351bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 352bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Don't clone these from the base event into the exception event. */ 353bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final String[] DONT_CLONE_INTO_EXCEPTION = { 354bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events._SYNC_ID, 355bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA1, 35602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA2, 35702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA3, 35802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA4, 35902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA5, 36002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA6, 361bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA7, 36202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA8, 363c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA9, 364c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA10, 365bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden }; 366bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 367bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** set to 'true' to enable debug logging for recurrence exception code */ 368bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final boolean DEBUG_EXCEPTION = false; 369bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 370dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 371e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 372e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 3738bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 3748bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 375420b7fb569773ae573fbe90c3a9c522d4c368863Erik @VisibleForTesting 376420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected CalendarAlarmManager mCalendarAlarm; 377a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 378a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 379a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 380a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 381dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 382a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 383a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 384a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 385dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 386dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 387dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 388dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 389dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 390a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 391a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 392a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 393a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 3949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 3969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 3979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 3999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 407420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 411420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 413420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void verifyAccounts() { 4199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 4209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff onAccountsUpdated(AccountManager.get(getContext()).getAccounts()); 4219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4298bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4308bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4318bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4328bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 433e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 434e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 435e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 436e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 437e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 438e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 439e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 4408bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4418bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 4449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 445ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 446ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 447ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 448f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 449f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 450f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 451ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 452ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 453ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 455ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 4568bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 4578bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 458dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 459e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 460e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 461ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 462ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4644caf8d015918f619a67d321a152f150a01022717Andy McFadden mMetaData = new MetaData(mDbHelper); 4654caf8d015918f619a67d321a152f150a01022717Andy McFadden mInstancesHelper = new CalendarInstancesHelper(mDbHelper, mMetaData); 4664caf8d015918f619a67d321a152f150a01022717Andy McFadden 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 4699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 4719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 4739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 4779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 478e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 4799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 480ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 481ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 482420b7fb569773ae573fbe90c3a9c522d4c368863Erik // This is pulled out for testing 483420b7fb569773ae573fbe90c3a9c522d4c368863Erik initCalendarAlarm(); 484e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 485e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 4868bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 4889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 490420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected void initCalendarAlarm() { 491420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = getOrCreateCalendarAlarmManager(); 492420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.getScheduleNextAlarmWakeLock(); 493e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 494e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 495e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 496420b7fb569773ae573fbe90c3a9c522d4c368863Erik if (mCalendarAlarm == null) { 497420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = new CalendarAlarmManager(mContext); 498e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 499420b7fb569773ae573fbe90c3a9c522d4c368863Erik return mCalendarAlarm; 500e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 501e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 502ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 503ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 504ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 505ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 506ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 507ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 508ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 509ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 510ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 511ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 512ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 513ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 514ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 515ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 516ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 517ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 5189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 521315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 5229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 5249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 5269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 5299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 5319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 532ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 5339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 537315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 538315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 539315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 540315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 541315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 542315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 543315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 544315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 5459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 5469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 5479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 548315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 549ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 550315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 551315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 552a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 553315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 554315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 555315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 556ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 557315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 558315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 559ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 560315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 561ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 562ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 563ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 564ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 565420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 566ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 567ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 568f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 569f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 570f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 571ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 572ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 573ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 574ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 575ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 576f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 577f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 578f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 579ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 581ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 582ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 583315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 584ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 585ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 5863443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 587ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 588315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 589ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 590ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 591ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 592ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 593ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 594ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 595ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 5963443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 5973443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 598ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 599ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 600ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 601ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 602ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6033443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 604f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 605f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 606f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 607f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 608f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 609f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 610f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 611ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6123443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 613ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 614ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 615ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 616ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 617ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 618ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 619ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 620ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 621ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 622ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 623ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 624f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 625f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 626f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 627ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 628ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 629ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 630ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 631ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 632ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 633f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 634f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 635f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 636ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 637ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 638ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 639ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 640ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 641ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 642ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 643ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 644ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 645b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 646b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 647ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 648b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 649dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 650ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 651ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 652ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 653ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 654ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 655ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 656ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 657ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 660ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 661f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 662f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 663f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 664ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 665ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 6669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 667ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 668ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 669ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 671315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 672315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 673ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 674ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 675ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 676ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 677ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 67825e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 679ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 680315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 681315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 682ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 683ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 684f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 685f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 686f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 687ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 688ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 690315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 691315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 692315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 693315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 694315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 695ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 6969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 6979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 6989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 6999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 700315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 701315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7071f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7101f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7111f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7121f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7131f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7141f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7151f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 7162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* selection */, null, 7172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* sort */, 718d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 719315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 7202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik instancesTimezone, isHomeTimezone()); 7211f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 7221f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 7231f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 7241f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7251f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 727420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 7289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 732b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 7339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 7349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 735b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik mContentResolver.notifyChange(CalendarContract.CONTENT_URI, null, syncToNetwork); 7369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 7399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 7409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 741ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 742ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 7439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 7469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 7489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 7499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 750315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 7519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 7539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 7549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 7559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 7569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 7579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 7591ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 7619ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 7629ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 7639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 7651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 767636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 768b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 7699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 77019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 77119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 77219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 77319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 7749ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 7759ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 77619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 77719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 77819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 77919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 780636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 781b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 78219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 78319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 7849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 78543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 786b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 7879ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 7889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 79043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 791b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 792636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 793b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 7949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 8019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 811315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 8122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceQuery(qb, begin, end, projection, selection, selectionArgs, 8132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik sortOrder, match == INSTANCES_BY_DAY, false /* don't force an expansion */, 814315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 81581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 81681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 81781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 81881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 81981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 82081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 82181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 82281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 82381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 82481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 82581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 82681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 82781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 82881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 829315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 83081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 83181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 8322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceSearchQuery(qb, begin, end, query, projection, selection, 8332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 834315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8356db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 8369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 8379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 8389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 8409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 8429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 8469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 8489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 850315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 851315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 852315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 85402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 856b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(Tables.EVENTS + "." + Events._ID + "=" 857b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio + Tables.ATTENDEES + "." + Attendees.EVENT_ID); 8589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 86002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 862636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 863b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 866b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 86902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 871636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 872b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 875b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 877b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 880b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 882b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 8839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 8849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 886b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 8879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 888636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 889b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 8909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 892b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 8939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 895b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 896636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 897b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 8989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 899315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 900b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 901315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 902315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 9099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 914ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 91539c65e5716e21e863d8de587d139dae85f99422fFred Quintana if (projection != null && projection.length == 1 91639c65e5716e21e863d8de587d139dae85f99422fFred Quintana && BaseColumns._COUNT.equals(projection[0])) { 91739c65e5716e21e863d8de587d139dae85f99422fFred Quintana qb.setProjectionMap(sCountProjectionMap); 91839c65e5716e21e863d8de587d139dae85f99422fFred Quintana } 91939c65e5716e21e863d8de587d139dae85f99422fFred Quintana 920ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 921ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 922ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 923ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 924ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 925ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 926ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 927ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 932b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik c.setNotificationUri(mContentResolver, CalendarContract.Events.CONTENT_URI); 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 9409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 9419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 9429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 9439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 9449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 9469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 9479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 948d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 949315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 950315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 9519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 9542ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik long rangeEnd, String[] projection, String selection, String[] selectionArgs, 9552ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String sort, boolean searchByDay, boolean forceExpansion, 9562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 95881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 9609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 9619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 9629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 963315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 9669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 9679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 9689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 969315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 970315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 971b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 9729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 9739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 974315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 975315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 976b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 9779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9782ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 9792ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = new String[] {String.valueOf(rangeEnd), 9808335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 9812ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 9822ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 9832ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 9842ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 9852ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 9862ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 9872ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 9888335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 9897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 9909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 99281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 9932ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * Combine a set of arrays in the order they are passed in. All arrays must 9942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * be of the same type. 9952ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 9962ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static <T> T[] combine(T[]... arrays) { 9972ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (arrays.length == 0) { 9982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik throw new IllegalArgumentException("Must supply at least 1 array to combine"); 9992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int totalSize = 0; 10022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize += array.length; 10042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik T[] finalArray = (T[]) (Array.newInstance(arrays[0].getClass().getComponentType(), 10072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize)); 10082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int currentPos = 0; 10102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int length = array.length; 10122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik System.arraycopy(array, 0, finalArray, currentPos, length); 10132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik currentPos += array.length; 10142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return finalArray; 10162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 1019dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1020dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1021dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1022dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1023dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1024dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1025dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1026dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1027dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1028dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1029dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 103081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1031dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1032dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 103381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 103481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 103581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 103681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 103781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 103881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1039dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1040dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1041dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1042dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1043dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1044dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1045dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1046dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1047dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1048dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1049dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1050dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1051dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1052dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 105381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 105481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 105581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 105681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 105781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 105881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 105981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 106081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 106181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 106281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 106381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 106481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 106581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 106681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 106781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 106881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 106981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 107081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 107181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 107281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * approximation of full-text search across multiple columns. 107381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 107481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 107581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 107681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 107781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 107881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 107981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 108081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 108181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 108281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 108381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 108481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1085dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1086dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1087dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 108881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 108981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 109081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 109181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 109218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 109318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 109418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 109518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 109681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 109781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 109881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 109981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 110081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 110181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 110218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 110318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 110481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 110518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 110618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 110718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 110881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1109f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1110f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 111118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 111281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 111581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 111781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 111881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 11192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selection, String[] selectionArgs, String sort, boolean searchByDay, 11202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 112118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 112281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 112381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1124dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 11252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 11262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 11272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 11282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 11292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 11302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 11312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 11322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 113318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 113418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1135dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1136dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 113781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 113881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 113981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1140315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 114181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 114281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 114381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 114481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 114581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 114618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 114718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 114856292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 114956292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1150315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1151315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1152315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 115356292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1154b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 115681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 115718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 115818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 115956292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 116056292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1161315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1162315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1163315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 116456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1165b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 116681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 116781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 116818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 116918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang Instances._ID /* groupBy */, searchWhere /* having */, sort); 117081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 117181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 11726db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1173315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1174315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 117581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 11766db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 117743556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 117843556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1179315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1180192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 118143556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 118243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1183192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 118443556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1185315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1186315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1187b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 11888335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 11898335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 11908335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 11916db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 11929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 11949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 11959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 11969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * table. Acquires the database lock and calls 11979ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #acquireInstanceRangeLocked(long, long, boolean, boolean, String, boolean)}. 11989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 11999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1202d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1203315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1204315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1206d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1207315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1208315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 12099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 12109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1211315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1212315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 12139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 12149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 12159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 12169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 12229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1226315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1227315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1228315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1230420b7fb569773ae573fbe90c3a9c522d4c368863Erik void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1231315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 12329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 12339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 12349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1235315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1236315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1237315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1238315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1239315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 12409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 12419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 12429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 12439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 12449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 12459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 12469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 12479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 12489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 12499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 12549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 12559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 12569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 12579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1258315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1259315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1260315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1261315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1264315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 12657be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 12667be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 12677be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 12687be45683e367bd6897daf6444b03be938f8f5eaaErik } 1269315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1270315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1271315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1272d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1274b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1275f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 12766db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1279f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1280315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1281315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 12829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 12847be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 12857be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 12867be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 12877be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 12887be45683e367bd6897daf6444b03be938f8f5eaaErik 12897be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1290315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 12917be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 12927be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 12937be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 12947be45683e367bd6897daf6444b03be938f8f5eaaErik } 1295315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 12969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 13009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 13029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 13039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 13049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 13079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 13089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 13099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1311f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 13139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 13149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 13199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1321f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1328f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1333315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 13399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 13409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 13529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 13549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 13566db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 13579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 135848587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 135948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1360315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1361315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 13649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1367b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /** 1368b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Determines if the event is recurrent, based on the provided values. 1369b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1370b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden public static boolean isRecurrenceEvent(String rrule, String rdate, String originalId, 1371b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId) { 1372b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return (!TextUtils.isEmpty(rrule) || 1373b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(rdate) || 1374b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalId) || 1375b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalSyncId)); 13769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1378646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1379646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1380646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1381646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1382646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * corrects the fields DTSTART, DTEND, and DURATION if necessary. Also checks to ensure that 1383646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * either both DTSTART and DTEND or DTSTART and DURATION are set for each event. 1384646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1385646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @param updatedValues The values to check and correct 1386646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1387646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1388646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private boolean fixAllDayTime(Uri uri, ContentValues updatedValues) { 1389646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1390646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (updatedValues.containsKey(Events.ALL_DAY) 1391646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik && updatedValues.getAsInteger(Events.ALL_DAY).intValue() == 1) { 1392646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtstart = updatedValues.getAsLong(Events.DTSTART); 1393646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Long dtend = updatedValues.getAsLong(Events.DTEND); 1394646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String duration = updatedValues.getAsString(Events.DURATION); 1395646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Time time = new Time(); 1396646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Cursor currentTimesCursor = null; 1397646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik String tempValue; 1398646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If a complete set of time fields doesn't exist query the db for them. A complete set 1399646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // is dtstart and dtend for non-recurring events or dtstart and duration for recurring 1400646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // events. 1401646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if(dtstart == null || (dtend == null && duration == null)) { 1402646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we have an id to search for, if not this is probably a new event 1403646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 2) { 1404646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = query(uri, 1405646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik ALLDAY_TIME_PROJECTION, 1406646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selection */, 1407646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* selectionArgs */, 1408646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik null /* sort */); 1409646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1410646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (!currentTimesCursor.moveToFirst() || 1411646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.getCount() != 1) { 1412646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Either this is a new event or the query is too general to get data 1413646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // from the db. In either case don't try to use the query and catch 1414646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // errors when trying to update the time fields. 1415646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1416646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor = null; 1417646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1418646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1419646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1420646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1421646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1422646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Ensure dtstart exists for this event (always required) and set so h,m,s are 0 if 1423646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // necessary. 1424646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // TODO Move this somewhere to check all events, not just allDay events. 1425646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtstart == null) { 1426646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1427646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields, we'd like to know if a field is empty 1428646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1429646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTSTART_INDEX); 1430646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1431646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtstart = Long.valueOf(tempValue); 1432646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1433646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1434646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("Event has no DTSTART field, the db " + 1435646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "may be damaged. Set DTSTART for this event to fix."); 1436646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1437646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 1438646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTSTART cannot be empty for new events."); 1439646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1440646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1441646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1442646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtstart.longValue()); 1443646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1444646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1445646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1446646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1447646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTSTART, time.toMillis(true)); 1448646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1449646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1450646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1451646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // If dtend exists for this event make sure it's h,m,s are 0. 1452646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend == null && currentTimesCursor != null) { 1453646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // getLong returns 0 for empty fields. We'd like to know if a field is empty 1454646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // so getString is used instead. 1455646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik tempValue = currentTimesCursor.getString(ALLDAY_DTEND_INDEX); 1456646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik try { 1457646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = Long.valueOf(tempValue); 1458646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } catch (NumberFormatException e) { 1459646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = null; 1460646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1461646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1462646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (dtend != null) { 1463646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1464646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.set(dtend.longValue()); 1465646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1466646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1467646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1468646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1469646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik dtend = time.toMillis(true); 1470646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DTEND, dtend); 1471646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1472646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1473646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1474646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1475646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (currentTimesCursor != null) { 1476646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null) { 1477646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = currentTimesCursor.getString(ALLDAY_DURATION_INDEX); 1478646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1479646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik currentTimesCursor.close(); 1480646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1481646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1482646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration != null) { 1483646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int len = duration.length(); 1484646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1485646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * in the seconds format, and if so converts it to days. 1486646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1487646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (len == 0) { 1488646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = null; 1489646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else if (duration.charAt(0) == 'P' && 1490646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration.charAt(len - 1) == 'S') { 1491646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1492646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1493646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik duration = "P" + days + "D"; 1494646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik updatedValues.put(Events.DURATION, duration); 1495646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1496646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1497646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1498646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1499646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (duration == null && dtend == null) { 1500646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 1501646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik "an event."); 1502646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1503646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1504646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1505646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1506646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1507bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1508bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1509bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Determines whether the strings in the set name columns that may be overridden 1510bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * when creating a recurring event exception. 1511bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1512bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This uses a white list because it screens out unknown columns and is a bit safer to 1513bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * maintain than a black list. 1514bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1515bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private void checkAllowedInException(Set<String> keys) { 1516bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : keys) { 1517bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!ALLOWED_IN_EXCEPTION.contains(str.intern())) { 1518bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions can't overwrite " + str); 1519bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1520bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1521bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1522bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1523bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1524bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Prepares an update to a recurrent event so it will stop just before a new series 1525bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * begins. This is useful when modifying "this and all future events". 1526bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1527bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * The "until" time is specified in the rrule in UTC. If the original event was an "all day" 1528bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event, we also need to convert the dtstart to UTC. 1529bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1530bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param values Event description; must include EVENT_TIMEZONE and DTSTART. 1531bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param endTimeMillis The time before which the event must end (i.e. the start time of the 1532bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event instance). 1533bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return Values to apply to the existing event. 1534bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1535bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static ContentValues setRecurrenceEnd(ContentValues values, long endTimeMillis) { 1536bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1537bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String oldRrule = values.getAsString(Events.RRULE); 1538bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1539bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden EventRecurrence eventRecurrence = new EventRecurrence(); 1540bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden eventRecurrence.parse(oldRrule); 1541bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1542bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time untilTime = new Time(); 1543bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time dtstart = new Time(); 1544bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1545bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The "until" time must be in UTC time in order for Google calendar 1546bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // to display it properly. For all-day events, the "until" time string 1547bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // must include just the date field, and not the time field. The 1548bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // repeating events repeat up to and including the "until" time. 1549bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden untilTime.timezone = Time.TIMEZONE_UTC; 1550bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = values.getAsString(Events.EVENT_TIMEZONE); 1551bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.set(values.getAsLong(Events.DTSTART)); 1552bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1553bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Subtract one second from the exception begin time to get the "until" time. 1554bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden untilTime.set(endTimeMillis - 1000); // subtract one second (1000 millis) 1555bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (allDay) { 1556bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden untilTime.hour = untilTime.minute = untilTime.second = 0; 1557bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden untilTime.allDay = true; 1558bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden untilTime.normalize(false); 1559bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1560bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.hour = dtstart.minute = dtstart.second = 0; 1561bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.allDay = true; 1562bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = Time.TIMEZONE_UTC; 1563bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1564bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden eventRecurrence.until = untilTime.format2445(); 1565bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1566bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues updateValues = new ContentValues(); 1567bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.RRULE, eventRecurrence.toString()); 1568bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.DTSTART, dtstart.normalize(true)); 1569bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return updateValues; 1570bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1571bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1572bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1573bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Handles insertion of an exception to a recurring event. 1574bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1575bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * There are two modes, selected based on the presence of "rrule" in modValues: 1576bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <ol> 1577bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Create a single instance exception ("modify current event only"). 1578bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Cap the original event, and create a new recurring event ("modify this and all 1579bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * future events"). 1580bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * </ol> 1581bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This may be used for "modify all instances of the event" by simply selecting the 1582bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * very first instance as the exception target. In that case, the ID of the "new" 1583bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event will be the same as the originalEventId. 1584bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1585bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param originalEventId The _id of the event to be modified 1586bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param modValues Event columns to update 1587c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * @param callerIsSyncAdapter Set if the content provider client is the sync adapter 1588bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return the ID of the new "exception" event, or -1 on failure 1589bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1590c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden private long handleInsertException(long originalEventId, ContentValues modValues, 1591c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden boolean callerIsSyncAdapter) { 1592bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1593bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.i(TAG, "RE: values: " + modValues.toString()); 1594bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1595bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1596bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Make sure they have specified an instance via originalInstanceTime. 1597bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Long originalInstanceTime = modValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1598bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime == null) { 1599bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions must specify " + 1600bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.ORIGINAL_INSTANCE_TIME); 1601bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1602bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1603bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Check for attempts to override values that shouldn't be touched. 1604bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden checkAllowedInException(modValues.keySet()); 1605bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1606c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // If this isn't the sync adapter, set the "dirty" flag in any Event we modify. 1607c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!callerIsSyncAdapter) { 1608c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.put(Events.DIRTY, true); 1609c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1610c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1611bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Wrap all database accesses in a transaction. 1612bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.beginTransaction(); 1613bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Cursor cursor = null; 1614bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1615bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that there's an instance corresponding to the specified time 1616bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (does this matter? it's weird, but not fatal?) 1617bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1618bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Grab the full set of columns for this event. 1619bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor = mDb.query(Tables.EVENTS, null /* columns */, 1620bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(originalEventId) }, 1621bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1622bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor.getCount() != 1) { 1623bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event ID " + originalEventId + " lookup failed (count is " + 1624bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.getCount() + ")"); 1625bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1626bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1627bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden //DatabaseUtils.dumpCursor(cursor); 1628bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1629bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1630bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Verify that the original event is in fact a recurring event by checking for the 1631bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * presence of an RRULE. If it's there, we assume that the event is otherwise 1632bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * properly constructed (e.g. no DTEND). 1633bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1634bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.moveToFirst(); 1635bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int rruleCol = cursor.getColumnIndex(Events.RRULE); 1636bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (TextUtils.isEmpty(cursor.getString(rruleCol))) { 1637bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event has no rrule"); 1638bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1639bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1640bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1641bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: old RRULE is " + cursor.getString(rruleCol)); 1642bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1643bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1644bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Verify that the original event is not itself a (single-instance) exception. 1645bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int originalIdCol = cursor.getColumnIndex(Events.ORIGINAL_ID); 1646bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!TextUtils.isEmpty(cursor.getString(originalIdCol))) { 1647bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event is an exception"); 1648bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1649bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1650bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1651bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createSingleException = TextUtils.isEmpty(modValues.getAsString(Events.RRULE)); 1652bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1653bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: check for the presence of an existing exception on this event+instance? 1654bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The caller should be modifying that, not creating another exception. 1655bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Alternatively, we could do that for them.) 1656bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1657bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Create a new ContentValues for the new event. Start with the original event, 1658bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // and drop in the new caller-supplied values. This will set originalInstanceTime. 1659bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues values = new ContentValues(); 1660bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 1661bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1662b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: if we're changing this to an all-day event, we should ensure that 1663b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // hours/mins/secs on DTSTART are zeroed out (before computing DTEND). 1664b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // See fixAllDayTime(). 1665b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1666bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createNewEvent = true; 1667bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createSingleException) { 1668bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1669bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Save a copy of a few fields that will migrate to new places. 1670bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1671bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _id = values.getAsString(Events._ID); 1672bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _sync_id = values.getAsString(Events._SYNC_ID); 1673bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1674bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1675bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1676bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Wipe out some fields that we don't want to clone into the exception event. 1677bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1678bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : DONT_CLONE_INTO_EXCEPTION) { 1679bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(str); 1680bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1681bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1682bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1683bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Merge the new values on top of the existing values. Note this sets 1684bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * originalInstanceTime. 1685bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1686bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1687bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1688bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1689bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Copy some fields to their "original" counterparts: 1690bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id --> original_id 1691bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _sync_id --> original_sync_id 1692bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * allDay --> originalAllDay 1693bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1694bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this event hasn't been sync'ed with the server yet, the _sync_id field will 1695bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * be null. We will need to fill original_sync_id in later. (May not be able to 1696bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * do it right when our own _sync_id field gets populated, because the order of 1697bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * events from the server may not be what we want -- could update the exception 1698bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * before updating the original event.) 1699bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1700bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id is removed later (right before we write the event). 1701bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1702bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ID, _id); 1703bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_SYNC_ID, _sync_id); 1704bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ALL_DAY, allDay); 1705bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1706bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Mark the exception event status as "tentative", unless the caller has some 1707bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // other value in mind (like STATUS_CANCELED). 1708bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!values.containsKey(Events.STATUS)) { 1709bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.STATUS, Events.STATUS_TENTATIVE); 1710bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1711bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1712bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // We're converting from recurring to non-recurring. Clear out RRULE and replace 1713bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DURATION with DTEND. 1714c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.remove(Events.RRULE); 1715bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1716bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Duration duration = new Duration(); 1717bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String durationStr = values.getAsString(Events.DURATION); 1718bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1719bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden duration.parse(durationStr); 1720bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } catch (Exception ex) { 1721bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // NullPointerException if the original event had no duration. 1722bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DateException if the duration was malformed. 1723bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Bad duration in recurring event: " + durationStr, ex); 1724bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1725bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1726bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1727c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1728c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * We want to compute DTEND as an offset from the start time of the instance. 1729c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If the caller specified a new value for DTSTART, we want to use that; if not, 1730c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the DTSTART in "values" will be the start time of the first instance in the 1731c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * recurrence, so we want to replace it with ORIGINAL_INSTANCE_TIME. 1732c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1733c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long start; 1734c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.DTSTART)) { 1735c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.DTSTART); 1736c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1737c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1738c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.put(Events.DTSTART, start); 1739c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1740bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.DTEND, start + duration.getMillis()); 1741bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1742c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "RE: ORIG_INST_TIME=" + start + 1743c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ", duration=" + duration.getMillis() + 1744bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ", generated DTEND=" + values.getAsLong(Events.DTEND)); 1745bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1746bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1747bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1748bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're going to "split" the recurring event, making the old one stop before 1749bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * this instance, and creating a new recurring event that starts here. 1750bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1751bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * No need to fill out the "original" fields -- the new event is not tied to 1752bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * the previous event in any way. 1753bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1754bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this is the first event in the series, we can just update the existing 1755bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event with the values. 1756bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1757bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean canceling = (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 1758bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1759bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime.equals(values.getAsLong(Events.DTSTART))) { 1760bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1761bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Update fields in the existing event. Rather than use the merged data 1762bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * from the cursor, we just do the update with the new value set after 1763bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * removing the ORIGINAL_INSTANCE_TIME entry. 1764bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1765bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (canceling) { 1766bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: should we just call deleteEventInternal? 1767bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "Note: canceling entire event via exception call"); 1768bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1769bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1770bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: updating full event"); 1771bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1772bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden modValues.remove(Events.ORIGINAL_INSTANCE_TIME); 1773bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 1774bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1775bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden createNewEvent = false; // skip event creation and related-table cloning 1776bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1777bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1778bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: splitting event"); 1779bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1780bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1781bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1782bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Cap the original event so it ends just before the target instance. 1783bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1784bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues splitValues = setRecurrenceEnd(values, originalInstanceTime); 1785bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, splitValues, SQL_WHERE_ID, 1786bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1787bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1788bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1789bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Create the new event. We remove originalInstanceTime, because we're now 1790bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * creating a new event rather than an exception. 1791bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1792bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're always cloning a non-exception event (we tested to make sure the 1793bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event doesn't specify original_id, and we don't allow original_id in the 1794bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * modValues), so we shouldn't end up creating a new event that looks like 1795bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * an exception. 1796bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events.ORIGINAL_INSTANCE_TIME); 1799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1800c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long newEventId; 1803bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createNewEvent) { 1804bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events._ID); // don't try to set this explicitly 1805bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden validateEventData(values); 1806bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1807bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = mDb.insert(Tables.EVENTS, null, values); 1808bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (newEventId < 0) { 1809bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Unable to add exception to recurring event"); 1810bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Values: " + values); 1811bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1812bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1813bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1814bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: new ID is " + newEventId); 1815bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1816bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1817b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: do we need to do something like this? 1818b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden //updateEventRawTimesLocked(id, updatedValues); 1819b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1820b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1821b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Force re-computation of the Instances associated with the recurrence event. 1822b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1823b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, newEventId, true, mDb); 1824b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1825bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1826bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Some of the other tables (Attendees, Reminders, ExtendedProperties) reference 1827c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Event ID. We need to copy the entries from the old event, filling in the 1828c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * new event ID, so that somebody doing a SELECT on those tables will find 1829c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * matching entries. 1830bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1831bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden CalendarDatabaseHelper.copyEventRelatedTables(mDb, newEventId, originalEventId); 1832c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1833c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1834c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If we modified Event.selfAttendeeStatus, we need to keep the corresponding 1835c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * entry in the Attendees table in sync. 1836c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1837c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 1838c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1839c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * Each Attendee is identified by email address. To find the entry that 1840c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * corresponds to "self", we want to compare that address to the owner of 1841c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Calendar. We're expecting to find one matching entry in Attendees. 1842c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1843c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long calendarId = values.getAsLong(Events.CALENDAR_ID); 1844c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden cursor = mDb.query(Tables.CALENDARS, new String[] { Calendars.OWNER_ACCOUNT }, 1845c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(calendarId) }, 1846c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1847c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!cursor.moveToFirst()) { 1848c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.w(TAG, "Can't get calendar account_name for calendar " + calendarId); 1849c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1850c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden String accountName = cursor.getString(0); 1851c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ContentValues attValues = new ContentValues(); 1852c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.put(Attendees.ATTENDEE_STATUS, 1853c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.getAsString(Events.SELF_ATTENDEE_STATUS)); 1854c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1855c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (DEBUG_EXCEPTION) { 1856c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "Updating attendee status for event=" + newEventId + 1857c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden " name=" + accountName + " to " + 1858c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.getAsString(Attendees.ATTENDEE_STATUS)); 1859c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1860c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden int count = mDb.update(Tables.ATTENDEES, attValues, 1861c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Attendees.EVENT_ID + "=? AND " + Attendees.ATTENDEE_EMAIL + "=?", 1862c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden new String[] { String.valueOf(newEventId), accountName }); 1863b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (count != 1 && count != 2) { 1864b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // We're only expecting one matching entry. We might briefly see 1865b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // two during a server sync. 1866b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Log.e(TAG, "Attendee status update on event=" + newEventId + 1867b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden " name=" + accountName + " touched " + count + " rows"); 1868b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (false) { 1869b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This dumps PII in the log, don't ship with it enabled. 1870b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Cursor debugCursor = mDb.query(Tables.ATTENDEES, null, 1871b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.EVENT_ID + "=? AND " + 1872b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.ATTENDEE_EMAIL + "=?", 1873b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { String.valueOf(newEventId), accountName }, 1874b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden null, null, null); 1875b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden DatabaseUtils.dumpCursor(debugCursor); 1876b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden } 1877b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden throw new RuntimeException("Status update WTF"); 1878c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1879c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1880c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden cursor.close(); 1881c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1882bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1883b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1884b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Update any Instances changed by the update to this Event. 1885b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1886b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, originalEventId, false, mDb); 1887bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = originalEventId; 1888bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1889bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1890bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.setTransactionSuccessful(); 1891bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return newEventId; 1892bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } finally { 1893bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor != null) { 1894bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.close(); 1895bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1896bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.endTransaction(); 1897bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1898bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1899bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 19009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 1901b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 1902ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 19039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 19049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 19060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_INSERT, uri, values, callerIsSyncAdapter, match, 19070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik null /* selection */, null /* selection args */); 19089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 19109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 1912bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case SYNCSTATE: 19139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 19149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 19159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 19167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 1917c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 19187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 19199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 19209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 19219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 1923e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 1924e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff validateEventData(updatedValues); 1925e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 1926e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 19279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 19289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 19299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 19309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 19329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 19339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 19349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 19359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 19369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 19379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 19389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 19399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 19409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 19419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 194334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 194434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !updatedValues.containsKey(Events.ORIGINAL_ID)) { 194534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = getOriginalId(updatedValues 194634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsString(Events.ORIGINAL_SYNC_ID)); 194734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId != -1) { 194834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_ID, originalId); 194934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 195034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } else if (!updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 195134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && updatedValues.containsKey(Events.ORIGINAL_ID)) { 195234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = getOriginalSyncId(updatedValues 195334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsLong(Events.ORIGINAL_ID)); 195434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (!TextUtils.isEmpty(originalSyncId)) { 195534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_SYNC_ID, originalSyncId); 195634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 195734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 1958646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, updatedValues)) { 1959f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 1960f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 1961f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 1962f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 1963646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1964c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 19659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 19669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 19679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 1968f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 1969f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik true /* new event */, mDb); 19709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 19729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 19739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 19749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 19759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 19769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 19779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 19799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19808ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // if the Event Timezone is defined, store it as the original one in the 19818ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // ExtendedProperties table 19828ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) { 19838ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE); 19848ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 19858ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues expropsValues = new ContentValues(); 1986b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.EVENT_ID, id); 1987b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.NAME, 19888ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio EXT_PROP_ORIGINAL_TIMEZONE); 1989b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.VALUE, 1990b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik originalTimezone); 19918ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 19928ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Insert the extended property 19938ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues); 19948ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (exPropId == -1) { 19958ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 19968ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot add the original Timezone in the " 19978ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + "ExtendedProperties table for Event: " + id); 19988ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 19998ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } else { 20008ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Update the Event for saying it has some extended properties 20018ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues eventValues = new ContentValues(); 20028ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1"); 2003b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update("Events", eventValues, SQL_WHERE_ID, 20048ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio new String[] {String.valueOf(id)}); 20058ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (result <= 0) { 20068ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20078ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot update hasExtendedProperties column" 20088ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + " for Event: " + id); 20098ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20108ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20118ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20128ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 2013b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 2014b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 2015b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * The server might send exceptions before the event they refer to. When 2016b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * this happens, the originalId field will not have been set in the 2017b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * exception events (it's the recurrence events' _id field, so it can't be 2018b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * known until the recurrence event is created). When we add a recurrence 2019b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * event with a non-empty _sync_id field, we write that event's _id to the 2020b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * originalId field of any events whose originalSyncId matches _sync_id. 2021b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 202234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (values.containsKey(Events._SYNC_ID) && values.containsKey(Events.RRULE) 202334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !TextUtils.isEmpty(values.getAsString(Events.RRULE))) { 202434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String syncId = values.getAsString(Events._SYNC_ID); 202534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (TextUtils.isEmpty(syncId)) { 202634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik break; 202734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 202834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik ContentValues originalValues = new ContentValues(); 202934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalValues.put(Events.ORIGINAL_ID, id); 203034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik mDb.update(Tables.EVENTS, originalValues, Events.ORIGINAL_SYNC_ID + "=?", 203134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik new String[] {syncId}); 203234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 2033dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 20349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2036bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID: 2037bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long originalEventId = ContentUris.parseId(uri); 2038c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden id = handleInsertException(originalEventId, values, callerIsSyncAdapter); 2039bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden break; 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 20419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 20429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 2043c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 20449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 2045c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 2047fa332ecedc0c340109811552407142f6e4f600b2RoboErik String eventsUrl = values.getAsString(Calendars.CAL_SYNC1); 20481b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 20499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2051dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 20529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 20549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 20559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20587e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20599ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Attendees.EVENT_ID); 20609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 20619ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 20627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20639ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.attendeesInsert(values); 20649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 20669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 20679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 20719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 20749ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Reminders.EVENT_ID); 20759ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 20769ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 20777e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20789ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.remindersInsert(values); 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 20819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 20829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 20839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2084420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 20859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 20879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 20889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 20899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 20922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 20932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 20949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 2096b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (!values.containsKey(CalendarContract.ExtendedProperties.EVENT_ID)) { 20979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 20989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 20999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2101b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik final Long eventId = values 2102b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik .getAsLong(CalendarContract.ExtendedProperties.EVENT_ID); 21039ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 21049ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 21057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21069ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.extendedPropertiesInsert(values); 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 21099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 21109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 21146db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2115315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 21167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 21179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 21229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 21239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 21269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2128e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 212995229b60087d6c25f80bbcdebf47ac110c01df68RoboErik * Do some validation on event data before inserting. In particular make 213095229b60087d6c25f80bbcdebf47ac110c01df68RoboErik * sure calendar_id exists and dtend, duration, etc make sense for the type 213195229b60087d6c25f80bbcdebf47ac110c01df68RoboErik * of event (regular, recurrence, exception). Remove any unexpected fields. 2132e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 2133e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues to insert 2134e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2135e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private void validateEventData(ContentValues values) { 2136e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2137e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2138e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2139e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 214095229b60087d6c25f80bbcdebf47ac110c01df68RoboErik boolean hasCalId = !TextUtils.isEmpty(values.getAsString(Events.CALENDAR_ID)); 2141c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2142e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 214395229b60087d6c25f80bbcdebf47ac110c01df68RoboErik if (!hasCalId) { 214495229b60087d6c25f80bbcdebf47ac110c01df68RoboErik throw new IllegalArgumentException("New events must include a calendar_id."); 214595229b60087d6c25f80bbcdebf47ac110c01df68RoboErik } 2146e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2147e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2148e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2149e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2150e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2151e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is the recurrence rule 2152e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2153e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2154e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2155e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 2156e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2157e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence: " + values); 2158e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2159e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2160c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.remove(Events.ORIGINAL_SYNC_ID); 2161e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 2162e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2163e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2164e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2165e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2166e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2167e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2168e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2169e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2170e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2171e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2172e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 2173e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2174e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for recurrence exception: " + values); 2175e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2176e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2177e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2178e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2179e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2180e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2181e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2182e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2183e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2184e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2185e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2186e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2187e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 2188e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 2189e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff Log.e(TAG, "Invalid values for event: " + values); 2190e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2191e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 2192e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2193e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2194e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2195e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 21969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private void setEventDirty(long eventId) { 21979ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Object[] {eventId}); 21987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 220034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private long getOriginalId(String originalSyncId) { 220134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (TextUtils.isEmpty(originalSyncId)) { 220234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return -1; 220334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 220434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 220534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = -1; 220634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 220734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 220834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, 220934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._SYNC_ID + "=?", new String[] {originalSyncId}, null); 221034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 221134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalId = c.getLong(0); 221234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 221334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 221434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 221534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 221634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 221734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 221834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalId; 221934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 222034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 222134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private String getOriginalSyncId(long originalId) { 222234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId == -1) { 222334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return null; 222434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 222534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 222634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = null; 222734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 222834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 222934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, new String[] {Events._SYNC_ID}, 223034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._ID + "=?", new String[] {Long.toString(originalId)}, null); 223134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 223234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalSyncId = c.getString(0); 223334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 223434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 223534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 223634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 223734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 223834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 223934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalSyncId; 224034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 224134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 22429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 22449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 22459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 22469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2248f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2249f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2250f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2251f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2252f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2253f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 22549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 22559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 22569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 22579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 22589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 22599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 22609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 22619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 22629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 22639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2264f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2265f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2266f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 22679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 22689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 22709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 22769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 22799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 22809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 22819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 22829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 22839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 22869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 22879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 22889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 22959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 22999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 23099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 23109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 23119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 23129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 23139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 23149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 23169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 23189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 23199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 23209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 23219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 23229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 23239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 23249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2326f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2327f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2328f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 23339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 23419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 23429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 23439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 23449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 23469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 23479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2348f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2349f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2350f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 23519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 23549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 23559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 23569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 23579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 23619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 23659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 23669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 23679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 23689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 23719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 23729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 23739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 23749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 23759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 23799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 23819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 23829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 23839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 23919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2392b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2393b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 23949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 23979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 23989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 23999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 24009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 24019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 24029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 24039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 24049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 24059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 24069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 24079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 24089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 24109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 24129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 24139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 24169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 24179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 24189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 24199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 24209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 24219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 24229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 24239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 24249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 24259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 24269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 24279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 24289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2430f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2431f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2432f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2433f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2434f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2435f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2436b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik values.get(CalendarContract.Events.RRULE), e); 2437f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2438f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2439f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 24409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2441f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 24429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 24439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 24449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 24469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 24489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 24499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 24509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 24529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 24549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 24569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 24579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 24589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 24599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 24619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 24629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 24639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 24669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 24679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 24699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2471e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2472e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 2473e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues (in/out) 2474e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2475e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2476e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 24779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 24789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 24799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 24809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 24819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2486f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2487f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2488f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 24899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 24909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 24949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 24959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2496b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.EVENT_ID, eventId); 24979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 24999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 25019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 25029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 25039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 25049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 25079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 25089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 25099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 25129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 25139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 25149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 25159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 2516b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTSTART_2445, time.format2445()); 25179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 25209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 25219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 2522b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTEND_2445, time.format2445()); 25239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 25269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 25279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 25289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 25299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 25309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 25319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 25329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 25339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 2535b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.ORIGINAL_INSTANCE_TIME_2445, 2536b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 25379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 25409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 25429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 2543b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.LAST_DATE_2445, time.format2445()); 25449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 25479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2550b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2551b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2552ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 25539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 25549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 25560739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_DELETE, uri, null, callerIsSyncAdapter, match, 25570739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 25580739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 25599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 25642ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 25659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 25669323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2567dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2568dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2569dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2570dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 25719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 25739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 25747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 25750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection = appendSyncAccountToSelection(uri, selection); 25767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 25771ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 2578ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION, 25791ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 25807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 25821ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 25831ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 258410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2586420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 2587dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 25889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 25899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 25909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 25919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 25951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 25961ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 25971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 25981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 25991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 26001ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 26011ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 260210b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 26031ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 2604bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID2: 2605bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden { 2606bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // This will throw NumberFormatException on missing or malformed input. 2607bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden List<String> segments = uri.getPathSegments(); 2608bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long eventId = Long.parseLong(segments.get(1)); 2609bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long excepId = Long.parseLong(segments.get(2)); 2610bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that this is an exception instance (has an ORIGINAL_ID field 2611bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // that matches the supplied eventId) 2612bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return deleteEventInternal(excepId, callerIsSyncAdapter, false /* isBatch */); 2613bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 26149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 26159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2617b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 26187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2619b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, selection, selectionArgs); 26207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 26239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26242fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 26252fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 26262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 26287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2629b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 2630b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 26317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2632b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, null /* selection */, 26332fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 26347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 26379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2639b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, selection, selectionArgs); 26407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2641b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, selection, selectionArgs); 26427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26462fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 26472fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 26482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 26507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2651b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, SQL_WHERE_ID, 2652b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 26537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2654b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, null /* selection */, 26552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 26562fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 26592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 26602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 2661b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 26622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2663b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, selection, 2664b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 26652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26662fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26672fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 26682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 26692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 26702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 26712fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26722fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 26732fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2674b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 2675636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 26762fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2677b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, null /* selection */, 26782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 26797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2684b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 26857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2686b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.CALENDAR_ALERTS, uri, selection, selectionArgs); 26877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 26909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 26912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 26922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 26932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 26942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 26952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2697b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 2698b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 27012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik StringBuilder selectionSb = new StringBuilder(Calendars._ID + "="); 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 27039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 27099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2711595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 271274ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 27149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 27156db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2716315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 272310b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 27241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2725192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 27261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 27271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 2728b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 2729b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 2730636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 27311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 27321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 27331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 27341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 27351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 273648f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 27371ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 27381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 27391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 27401ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 27411ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 27421ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 2743b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origId = cursor.getString(EVENTS_ORIGINAL_ID_INDEX); 2744b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origSyncId = cursor.getString(EVENTS_ORIGINAL_SYNC_ID_INDEX); 2745b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (isRecurrenceEvent(rrule, rdate, origId, origSyncId)) { 27461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 27471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27481ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 274948f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 275048f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 2751bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // 2752bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The EVENTS_CLEANUP_TRIGGER_SQL trigger will remove all associated data 2753bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Attendees, Instances, Reminders, etc). 275448f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 2755b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 27561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 2757bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Event is on the server, so we "soft delete", i.e. mark as deleted so that 2758bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the sync adapter has a chance to tell the server about the deletion. After 2759bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the server sees the change, the sync adapter will do the "hard delete" 2760bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (above). 27611ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 27621b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 2763c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 2764b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 276502494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 276643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 276743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 2768b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 2769b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 2770b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 2771b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 2772b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 2773b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 27741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 27771ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 27781ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 27791ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27808f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 278110b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 2782420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 2783dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 278410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 27851ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 27861ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 27871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 27887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 27897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 27907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 27917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 27927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 27937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 27947e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 27957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 27967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 27977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 27989ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 27999ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final ContentValues values = new ContentValues(); 2800c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, "1"); 28017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 28027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 28037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 28049ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long id = c.getLong(ID_INDEX); 28059ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long event_id = c.getLong(EVENT_ID_INDEX); 28069ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(event_id); 28079ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.delete(table, SQL_WHERE_ID, new String[]{String.valueOf(id)}); 2808b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 2809b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 28107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 28117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 28137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 28147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 28167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 28187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 28197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 28207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 28217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 28227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 28237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 28247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 28257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 28267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 28277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 28287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 28297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 28309ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 28319ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final ContentValues dirtyValues = new ContentValues(); 2832c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik dirtyValues.put(Events.DIRTY, "1"); 28337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 28347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 28357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 28369ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long id = c.getLong(ID_INDEX); 28379ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long event_id = c.getLong(EVENT_ID_INDEX); 28389ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(event_id); 2839b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(table, values, SQL_WHERE_ID, new String[] {String.valueOf(id)}); 2840b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 2841b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 28427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 28437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 28457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 28467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 28487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 285074ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 28519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 28529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 28539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 285474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 285574ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 285674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 285774ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 285874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 28609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 28619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 28639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 28649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 28659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 28669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 28689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 28699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 287074ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 28719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2873fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private Cursor getCursorForEventIdAndProjection(String eventId, String[] projection) { 2874fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return mDb.query(Tables.EVENTS, 2875fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio projection, 2876fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio SQL_WHERE_ID, 2877fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { eventId }, 2878fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* group by */, 2879fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* having */, 2880fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio null /* order by*/); 2881fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2882fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2883fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 2884fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 2885fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2886fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 2887fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2888fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 2889fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2890fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 2891fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 2892fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 2893fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2894fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2895fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 2896fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 2897fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 2898fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 2899fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 2900fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 2901fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 2902fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 2903fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 2904fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 2905fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesStatusCancelUpdateMeanUpdate(String eventId, ContentValues values) { 2906fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio boolean isStatusCanceled = values.containsKey(Events.STATUS) && 2907fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 2908fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 2909fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Cursor cursor = null; 2910fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio try { 2911fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio cursor = getCursorForEventIdAndProjection(eventId, 2912b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { Events.ORIGINAL_SYNC_ID }); 2913fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (!cursor.moveToFirst()) { 2914fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2915fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "Cannot find Event with id: " + eventId); 2916fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2917fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 2918fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2919b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId = cursor.getString(0); 2920fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2921b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (!TextUtils.isEmpty(originalSyncId)) { 2922b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This event is an exception. See if the recurring event still exists. 2923b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return doesEventExistForSyncId(originalSyncId); 2924fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2925fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } finally { 2926fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio cursor.close(); 2927fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2928fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2929fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is the normal case, we just want an UPDATE 2930fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return true; 2931fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 2932fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 29339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: call calculateLastDate()! 29349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 29359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 2936b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 2937ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 29389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 29399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29400739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 29410739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_UPDATE, uri, values, callerIsSyncAdapter, match, 29420739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 29439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 29459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 294743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS 2948315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio && match != EVENTS && match != CALENDARS && match != PROVIDER_PROPERTIES) { 2949b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio throw new IllegalArgumentException("WHERE based updates not supported"); 29509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2951fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 29529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 29539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 29549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 29559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 29569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 29589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 29592ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 2960dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 29619323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2962dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2963dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2964dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 29659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 296743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 29689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 297043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 297143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 297243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null) { 297343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio throw new UnsupportedOperationException("Selection not permitted for " 297443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio + uri); 297543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 297643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 297743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 297843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 297943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 298043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 298143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 298243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 29834cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik if (selection != null && TextUtils.equals(selection,"_id=?")) { 29844cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik id = Long.parseLong(selectionArgs[0]); 29854cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik } else if (selection != null && selection.startsWith("_id=")) { 298643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 298743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 298843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 298943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 2990b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 299143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 299243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 299343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 2994c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Calendars.DIRTY, 1); 29952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 29969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 29979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 29989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 29999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3001b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 3002636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 30039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30043ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3005d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 30064067700dbedcf4c8a379c9ecba9b5603972b4607Andy McFadden if (values.containsKey(Calendars.VISIBLE)) { 3007d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3008d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3009d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3010d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3011420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 3012d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 30133ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3014dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 30153ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 30163ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 30179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 30189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 30209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 30219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = 0; 30237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (match == EVENTS_ID) { 30247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = ContentUris.parseId(uri); 3025a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else if (callerIsSyncAdapter) { 302643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: same remark as for CALENDARS/CALENDARS_ID case as this is not 302743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // sufficient to deal with all the "_id" case in selection 3028da641c374b00946f37bfe00e53bb292f4e0103d8RoboErik if (selection != null && selection.startsWith("_id=?")) { 3029da641c374b00946f37bfe00e53bb292f4e0103d8RoboErik id = Long.parseLong(selectionArgs[0]); 3030da641c374b00946f37bfe00e53bb292f4e0103d8RoboErik } else if (selection != null && selection.startsWith("_id=")) { 30317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // The ContentProviderOperation generates an _id=n string instead of 30327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // adding the id to the URL, so parse that out here. 30337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff id = Long.parseLong(selection.substring(4)); 3034a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } else { 3035a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // Sync adapter Events operation affects just Events table, not associated 3036a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff // tables. 3037646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(uri, values)) { 3038f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3039f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: Caller is sync adapter. " + 3040f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3041f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3042646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3043a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff return mDb.update("Events", values, selection, selectionArgs); 3044a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff } 30457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3046a7f687007ff4d0c30726bf86f717fde88f51b453Ken Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 30477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 3049c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 30507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Disallow updating the attendee status in the Events 30529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // table. In the future, we could support this but we 30539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // would have to query and update the attendees table 30549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to keep the values consistent. 30559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 30569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Updating " 30579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + Events.SELF_ATTENDEE_STATUS 30589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " in Events table is not allowed."); 30599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3060fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio String strId = String.valueOf(id); 3061fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // For taking care about recurrences exceptions cancelations, check if this needs 3062fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // to be an UPDATE or a DELETE 3063fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio boolean isUpdate = doesStatusCancelUpdateMeanUpdate(strId, values); 3064e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 3065e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // TODO: should extend validateEventData to work with updates and call it here 3066e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 30679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 3068f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3069f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not update event."); 3070f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 30729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3073646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik // Make sure we pass in a uri with the id appended to fixAllDayTime 3074646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Uri allDayUri; 3075646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (uri.getPathSegments().size() == 1) { 3076646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = ContentUris.withAppendedId(uri, id); 3077646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } else { 3078646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik allDayUri = uri; 3079646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 3080646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (fixAllDayTime(allDayUri, updatedValues)) { 3081f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3082f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "updateInTransaction: " + 3083f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 3084f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3085646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 30869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3087fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio int result; 30889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3089fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isUpdate) { 30909ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert // If a user made a change, possibly duplicate the event so we can do a partial 30919ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert // update. If a sync adapter made a change and that change marks an event as 30929ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert // un-dirty, remove any duplicates that may have been created earlier. 30939ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!callerIsSyncAdapter) { 30949ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(id); 30959ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } else { 30969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (values.containsKey(Events.DIRTY) 30979ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert && values.getAsInteger(Events.DIRTY) == 0) { 30989ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.removeDuplicateEvent(id); 30999ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 31009ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 3101fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio result = mDb.update(Tables.EVENTS, updatedValues, SQL_WHERE_ID, 3102fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { strId }); 3103fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (result > 0) { 3104fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio updateEventRawTimesLocked(id, updatedValues); 3105f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 3106f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik false /* not a new event */, mDb); 3107fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 310806c305d35741db303bd3aacd0eab5af8de0ab34eErik if (values.containsKey(Events.DTSTART) || 310906c305d35741db303bd3aacd0eab5af8de0ab34eErik values.containsKey(Events.STATUS)) { 311006c305d35741db303bd3aacd0eab5af8de0ab34eErik // If this is a cancellation knock it out 311106c305d35741db303bd3aacd0eab5af8de0ab34eErik // of the instances table 311206c305d35741db303bd3aacd0eab5af8de0ab34eErik if (values.containsKey(Events.STATUS) && 311306c305d35741db303bd3aacd0eab5af8de0ab34eErik values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 311406c305d35741db303bd3aacd0eab5af8de0ab34eErik String[] args = new String[] {String.valueOf(id)}; 311506c305d35741db303bd3aacd0eab5af8de0ab34eErik mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 311606c305d35741db303bd3aacd0eab5af8de0ab34eErik } 311706c305d35741db303bd3aacd0eab5af8de0ab34eErik 3118fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // The start time of the event changed, so run the 3119fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // event alarm scheduler. 3120fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 3121fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.d(TAG, "updateInternal() changing event"); 3122fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3123420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 31249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31253ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3126fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio sendUpdateNotification(id, callerIsSyncAdapter); 3127fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3128fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } else { 3129fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio result = deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3130420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3131fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio sendUpdateNotification(callerIsSyncAdapter); 31329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3133fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 31349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 31359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31362fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 31372fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31382fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31392fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 31419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 31429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 31437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3145b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.ATTENDEES, values, SQL_WHERE_ID, 314683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 31477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3148b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.ATTENDEES, values, uri, null /* selection */, 31492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 31507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 31532fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31542fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31562fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 31572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 31589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3159b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 3160636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 31632fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 31642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 3165b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 31669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31672fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 31682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3173b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = mDb.update(Tables.REMINDERS, values, SQL_WHERE_ID, 317483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 31757e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3176b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = updateInTable(Tables.REMINDERS, values, uri, null /* selection */, 31772fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 31787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 31809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 31819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 31829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 31839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 31849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3185420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 31867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 31879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 31892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 31902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 31912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3194b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.EXTENDED_PROPERTIES, values, SQL_WHERE_ID, 3195636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3197b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.EXTENDED_PROPERTIES, values, uri, 3198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio null /* selection */, null /* selectionArgs */); 31997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 320183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 320283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 320383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 3204420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 320583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 320683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 320783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 3208420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(true); 320983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 321083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 32119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3212315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 3213315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (selection == null) { 3214315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection cannot be null for " + uri); 3215315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3216315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 3217315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 3218315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3219315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3220315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 3221315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3222315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 3223315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 3224315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 3225315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3226315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3227315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 3228315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 3229315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3230315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 3231315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 3232b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 3233315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3234315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 3235f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "home", set the Instances 3236f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the previous 3237f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "auto", set the Instances 3238f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the current 3239f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // device one 3240f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone Instances is set AND if we are in "home" 3241f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone type, then save the timezone Instance into 3242f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // "previous" too 3243315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 3244315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 3245315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 3246315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 3247315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 3248315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 3249315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 3250315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 3251315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 3252315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 3253315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 3254315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3255315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 3256d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 3257315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 3258315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3259d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3260315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3261315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 3263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 3264315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 3265315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 3266315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 3267315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3268d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3269315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3270315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3271315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3272315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3273315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 3274315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 3275315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 3276315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 3277315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 3278315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 3279315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 3280315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 3281d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 3282315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 3283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 3284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3285d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3286315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3287315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3289315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3290315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 3291315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3292315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 32939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 32949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 32959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 32989ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendAccountFromParameterToSelection(String selection, Uri uri) { 3299b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3300b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 3301b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3302b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 3303595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 33049ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 33059ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(Calendars.ACCOUNT_NAME + "=") 33069ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountName)) 33079ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" AND ") 33089ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(Calendars.ACCOUNT_TYPE) 33099ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" = ") 33109ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountType)); 33119ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 3312595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 33139ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 33149ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 33159ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 33169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 33179ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendLastSyncedColumnToSelection(String selection, Uri uri) { 33189ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (getIsCallerSyncAdapter(uri)) { 33199ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 3320595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 33219ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 3322b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sb.append(CalendarContract.Events.LAST_SYNCED).append(" = 0"); 33239ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 3324595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3325595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 33269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 3327b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3328b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 3329b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3330b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 33319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 3332b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Calendars.ACCOUNT_NAME 3333b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + "=" + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3334b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Calendars.ACCOUNT_TYPE + "=" 33350739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountType)); 33369ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 33370739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 33380739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return selection; 33390739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 33400739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 33410739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 33420739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private String appendSyncAccountToSelection(Uri uri, String selection) { 33430739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountName = QueryParameterUtils.getQueryParameter(uri, 3344b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 33450739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountType = QueryParameterUtils.getQueryParameter(uri, 3346b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 33470739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (!TextUtils.isEmpty(accountName)) { 3348b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Events.ACCOUNT_NAME + "=" 33490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3350b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events.ACCOUNT_TYPE + "=" 33519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 33529ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 33539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 33549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 33559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33589ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendSelection(StringBuilder sb, String selection) { 33599ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!TextUtils.isEmpty(selection)) { 33609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(" AND ("); 33619ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(selection); 33629ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(')'); 33639ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 33649ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return sb.toString(); 33659ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 33669ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 33670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik /** 33680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * Verifies that the operation is allowed and throws an exception if it 33690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * isn't. This defines the limits of a sync adapter call vs an app call. 3370c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik * 33710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param type The type of call, {@link #TRANSACTION_QUERY}, 33720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_INSERT}, {@link #TRANSACTION_UPDATE}, or 33730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_DELETE} 33740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param uri 33750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param values 33760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param isSyncAdapter 33770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik */ 33780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyTransactionAllowed(int type, Uri uri, ContentValues values, 33790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik boolean isSyncAdapter, int uriMatch, String selection, String[] selectionArgs) { 33800739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (type) { 33810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_QUERY: 33820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 33830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_INSERT: 33840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 33850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException( 33860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Inserting into instances not supported"); 33870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 3388c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 3389c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 33900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 33910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 33920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 33930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 33940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 33950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 33960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 33970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 33980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_UPDATE: 33990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 34000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Updating instances not supported"); 34010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 3402c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 3403c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 34040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 34050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 34060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 34070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 34080739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 34090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 34100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34110739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 34120739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_DELETE: 34130739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 34140739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Deleting instances not supported"); 34150739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 34170739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 34180739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 34190739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34200739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 34210739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34220739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34230739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 34240739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyHasAccount(Uri uri, String selection, String[] selectionArgs) { 3425c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = QueryParameterUtils.getQueryParameter(uri, Calendars.ACCOUNT_NAME); 34260739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String accountType = QueryParameterUtils.getQueryParameter(uri, 3427c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 34280739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 34290739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (selection != null && selection.startsWith(ACCOUNT_SELECTION_PREFIX)) { 34300739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountName = selectionArgs[0]; 34310739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountType = selectionArgs[1]; 34320739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34330739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34340739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 34350739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException( 34360739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Sync adapters must specify an account and account type: " + uri); 34370739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34380739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34390739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 3440c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private void verifyColumns(ContentValues values, int uriMatch) { 3441c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 3442c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik return; 3443c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3444c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String[] columns; 3445c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik switch (uriMatch) { 3446c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 3447c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 3448c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 3449c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 3450c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = Events.PROVIDER_WRITABLE_COLUMNS; 3451c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3452c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik default: 3453c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = PROVIDER_WRITABLE_DEFAULT_COLUMNS; 3454c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3455c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3456c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 3457c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik for (int i = 0; i < columns.length; i++) { 3458c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values.containsKey(columns[i])) { 3459c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik throw new IllegalArgumentException("Only the provider may write to " + columns[i]); 3460c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3461c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3462c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3463c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 34640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyNoSyncColumns(ContentValues values, int uriMatch) { 3465c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 34660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 34670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String[] syncColumns; 34690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (uriMatch) { 34700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS: 34710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS_ID: 34720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES: 34730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES_ID: 3474c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Calendars.SYNC_WRITABLE_COLUMNS; 3475c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3476c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 3477c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 3478c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 3479c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 3480c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Events.SYNC_WRITABLE_COLUMNS; 34810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 34820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik default: 34830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik syncColumns = SYNC_WRITABLE_DEFAULT_COLUMNS; 34840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 34850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 34860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik for (int i = 0; i < syncColumns.length; i++) { 34880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (values.containsKey(syncColumns[i])) { 34890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException("Only sync adapters may write to " 34900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + syncColumns[i]); 34910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 34940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 34959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 34969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 34979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 34989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 3499c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik new String[] {Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, 3500fa332ecedc0c340109811552407142f6e4f600b2RoboErik Calendars.CAL_SYNC1, Calendars.SYNC_EVENTS}, 35019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 35029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 35039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 35049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 35069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 35079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 3508ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 35099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3510ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 3511ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 3512ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 3513ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 3514ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 3515ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 3516ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 35179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 35189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 35199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35229535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 35239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 3524f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3525f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 3526f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 3527f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 35289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 35299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35319535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 35329535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 35339535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 35349535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 35359535627bf6295cd94447beb83e1aac41f50c3600Erik } 35369535627bf6295cd94447beb83e1aac41f50c3600Erik 35379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 35389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 35399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 35409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 35439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 35449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 35459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3547a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3548a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3549a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3550a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 3551dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 35529ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 3553a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3554dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 3555dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 3556dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 3557a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3558a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3559a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3560a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3561a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3562a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 3563a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 3564a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 3565a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3566a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 3567a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 35689ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param eventId the ID of the event that changed, or -1 for no specific event 35699ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 3570a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3571dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 3572dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 3573a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 3574a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 3575a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 3576a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 3577a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 3578dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 3579dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 3580dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 3581dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 3582dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 3583dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 3584dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 3585dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 3586dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 3587dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 3588dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 3589dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 3590dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 3591dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 3592dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 3593dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 3594dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 3595a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 3596dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 3597a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3598a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3599a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3600a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 3601a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 36029ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #sendUpdateNotification(boolean)} instead. 3603a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 36049ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @see #sendUpdateNotification(boolean) 3605a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3606a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 3607a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 3608b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.CONTENT_URI); 3609f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 3610f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 3611f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3612e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 3613a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3614a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 36150739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_QUERY = 0; 36160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_INSERT = 1; 36170739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_UPDATE = 2; 36180739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_DELETE = 3; 36190739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 36200739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:off 36210739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final String[] SYNC_WRITABLE_DEFAULT_COLUMNS = new String[] { 3622b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars.DIRTY, 3623b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars._SYNC_ID 36240739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik }; 3625c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String[] PROVIDER_WRITABLE_DEFAULT_COLUMNS = new String[] { 3626c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik }; 36270739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:on 36280739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 36299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 36309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 36319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 36322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS = 4; 36332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS_ID = 5; 36342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES = 6; 36352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES_ID = 7; 36362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS = 8; 36372ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS_ID = 9; 36382ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES = 10; 36392ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES_ID = 11; 36402ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS = 12; 36412ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_ID = 13; 36422ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_BY_INSTANCE = 14; 36432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_BY_DAY = 15; 36442ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE = 16; 36452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE_ID = 17; 36462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES = 18; 36472ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES_ID = 19; 36482ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_DAYS = 20; 36492ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM = 21; 36502ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM_REMOVE = 22; 36512ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int TIME = 23; 36522ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES = 24; 36532ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES_ID = 25; 36542ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH = 26; 36552ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH_BY_DAY = 27; 36562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int PROVIDER_PROPERTIES = 28; 3657bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID = 29; 3658bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID2 = 30; 36599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 36619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 3662f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final HashMap<String, String> sEventsProjectionMap; 366319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 36649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 36659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 36669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 3667315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 366839c65e5716e21e863d8de587d139dae85f99422fFred Quintana private static final HashMap<String, String> sCountProjectionMap; 36699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 3671b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/when/*/*", INSTANCES); 3672b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 3673b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 3674b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/searchbyday/*/*/*", 367581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 3676b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 3677b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events", EVENTS); 3678b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events/#", EVENTS_ID); 3679b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities", EVENT_ENTITIES); 3680b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 3681b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars", CALENDARS); 3682b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars/#", CALENDARS_ID); 3683b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 3684b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 3685b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees", ATTENDEES); 3686b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees/#", ATTENDEES_ID); 3687b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders", REMINDERS); 3688b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders/#", REMINDERS_ID); 3689b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 3690b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties/#", 3691b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik EXTENDED_PROPERTIES_ID); 3692b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 3693b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 3694b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/by_instance", 3695b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 3696b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate", SYNCSTATE); 3697b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 3698b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, CalendarAlarmManager.SCHEDULE_ALARM_PATH, 3699420b7fb569773ae573fbe90c3a9c522d4c368863Erik SCHEDULE_ALARM); 3700b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, 3701b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 3702b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time/#", TIME); 3703b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time", TIME); 3704b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "properties", PROVIDER_PROPERTIES); 3705b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID); 3706b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2); 37079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 370839c65e5716e21e863d8de587d139dae85f99422fFred Quintana /** Contains just BaseColumns._COUNT */ 370939c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap = new HashMap<String, String>(); 371039c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)"); 371139c65e5716e21e863d8de587d139dae85f99422fFred Quintana 37129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 37139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 371402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME); 371502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_TYPE, Events.ACCOUNT_TYPE); 3716c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.TITLE, Events.TITLE); 3717c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 3718c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 3719c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.STATUS, Events.STATUS); 372002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 3721c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 3722c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART); 3723c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTEND, Events.DTEND); 3724c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 3725c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 3726c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DURATION, Events.DURATION); 3727c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 3728c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 3729c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 3730c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 3731c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, Events.HAS_EXTENDED_PROPERTIES); 3732c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RRULE, Events.RRULE); 3733c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RDATE, Events.RDATE); 3734c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXRULE, Events.EXRULE); 3735c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXDATE, Events.EXDATE); 3736c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 373734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 3738c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, Events.ORIGINAL_INSTANCE_TIME); 3739c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 3740c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 3741c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 3742c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 3743c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, Events.GUESTS_CAN_INVITE_OTHERS); 3744c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 3745c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 3746c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 3747c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DELETED, Events.DELETED); 374802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 37499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3750e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 37511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 37521ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 37531ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 37549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 3755c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 375602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL); 3757c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 375802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 3759c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 376002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 376102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 376202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 376302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND); 376402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 37659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3766982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 3767e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 3768e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 3769982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3770e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 3771982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 3772c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events._ID, Events._ID); 377302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 377402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 377502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 377602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 377702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 377802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 37799ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 378002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 378102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 378202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 378302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 378402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 378502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 378602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 378702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 378802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 378902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 379002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 379102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 379202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 3793c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY); 37949ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 37959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 379646f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 3797c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.TITLE, Events.TITLE); 3798c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 3799c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 3800c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.STATUS, Events.STATUS); 380102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 3802c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 3803c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTSTART, Events.DTSTART); 3804c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTEND, Events.DTEND); 3805c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 3806c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 3807c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DURATION, Events.DURATION); 3808c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 3809c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 3810c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 3811c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 3812c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, 3813c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.HAS_EXTENDED_PROPERTIES); 3814c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RRULE, Events.RRULE); 3815c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RDATE, Events.RDATE); 3816c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXRULE, Events.EXRULE); 3817c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXDATE, Events.EXDATE); 3818c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 381934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 3820c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, 3821c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_INSTANCE_TIME); 3822c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 3823c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 3824c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 3825c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 3826c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, 3827c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.GUESTS_CAN_INVITE_OTHERS); 3828c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 3829c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 3830c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 3831c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DELETED, Events.DELETED); 383219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 383319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 383402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 383502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 383602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 383702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 383802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 383902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 38409ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 384102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 384202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 384302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 3844470aa5bc291ca33d51dda356f38ac2954026da9aAlon Albert sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY); 38459ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 3846fa332ecedc0c340109811552407142f6e4f600b2RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 384702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 384802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 384902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 385002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 385102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 385202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 385302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 385402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 385502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 385619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 38579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 38581b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 38599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 38609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 38619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 38629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 38639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 38649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 38659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 38669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 38679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 38699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 38709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 38719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 38729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 38739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 38749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 38759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 387602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 387702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 38789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 38809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 38819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 38829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 38839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 38849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 38869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 38879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 38889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 38899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 38909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 38919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 38929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 3893315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3894315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 3895315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 3896315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 3897315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 38989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 39019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make sure that there are no entries for accounts that no longer 39029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * exist. We are overriding this since we need to delete from the 39039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 39047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 39057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 39069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 3907f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik @Override 39089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 3909ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 3910ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 3911ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 3912ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 3913ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 3914ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 39159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 391646f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 391746f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 39189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 39199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 39209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 39219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 39239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 39259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 39269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3927b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio for (String table : new String[]{Tables.CALENDARS}) { 3928ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 39299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 39307cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Cursor c = mDb.rawQuery("SELECT DISTINCT " + 39312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_NAME + 39327cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 39332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + 39347cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 39359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 39364cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // ACCOUNT_TYPE_LOCAL is to store calendars not associated 39374cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // with a system account. Typically, a calendar must be 39384cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // associated with an account on the device or it will be 39394cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // deleted. 3940b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (c.getString(0) != null 3941b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && c.getString(1) != null 3942b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && !TextUtils.equals(c.getString(1), 3943b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.ACCOUNT_TYPE_LOCAL)) { 39449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 39459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 39469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 39479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 39519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 3954f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 3955f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 3956f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 39579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 3958b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 39599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 39619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 39629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 39639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 39649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39653ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 39663ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 3967dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 39689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3970636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 3971636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 3972636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 3973636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 3974636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 3975636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 3976636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 3977636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 3978636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 3979636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 3980636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 3981636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 3982636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 3983636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 3984636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 3985636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 3986636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 3987636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 3988636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 39899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 3990