CalendarProvider2.java revision 0332925aa9db8c4826327edd85030a4791b7a8e6
19f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/* 29f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 39f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Copyright 2006, The Android Open Source Project 49f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 59f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Licensed under the Apache License, Version 2.0 (the "License"); 69f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** you may not use this file except in compliance with the License. 79f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** You may obtain a copy of the License at 89f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 99f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** http://www.apache.org/licenses/LICENSE-2.0 109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Unless required by applicable law or agreed to in writing, software 129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** distributed under the License is distributed on an "AS IS" BASIS, 139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** See the License for the specific language governing permissions and 149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** limitations under the License. 169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff*/ 179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpackage com.android.providers.calendar; 199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 37a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 38a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 41b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract; 42b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Attendees; 43b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.CalendarAlerts; 44b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Calendars; 452f251c778c06d21ed7693a70f4a1268ff929242eRoboErikimport android.provider.CalendarContract.Colors; 46b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Events; 47b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Instances; 48b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Reminders; 49b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.SyncState; 509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 511edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 52192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 55ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 578d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.calendarcommon.DateException; 588d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.calendarcommon.Duration; 598d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.calendarcommon.EventRecurrence; 608d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.calendarcommon.RecurrenceProcessor; 618d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.calendarcommon.RecurrenceSet; 628d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 638d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 648d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.google.android.collect.Sets; 658d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.google.common.annotations.VisibleForTesting; 668d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 673b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.io.File; 682ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErikimport java.lang.reflect.Array; 693b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.lang.reflect.Method; 709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 71ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFaddenimport java.util.Iterator; 75dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 76bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFaddenimport java.util.Set; 779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 78dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 83b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik * is defined in {@link android.provider.CalendarContract}. 849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 888bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 89d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden static final boolean DEBUG_INSTANCES = false; 909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 917be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 92c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String ACCOUNT_SELECTION_PREFIX = Calendars.ACCOUNT_NAME + "=? AND " 93c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik + Calendars.ACCOUNT_TYPE + "=?"; 947be45683e367bd6897daf6444b03be938f8f5eaaErik 95f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final boolean PROFILE = false; 969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 978f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 1009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 1029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 1039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 105b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Events.ORIGINAL_ID, 106c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_SYNC_ID, 1079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1089ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 1099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 112b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_ID_INDEX = 3; 113b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4; 1147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] COLORS_PROJECTION = new String[] { 1162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_NAME, 1172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_TYPE, 1182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR_TYPE, 119387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik Colors.COLOR_KEY, 1202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR, 1212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_NAME_INDEX = 0; 1232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_TYPE_INDEX = 1; 1242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_TYPE_INDEX = 2; 1252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX_INDEX = 3; 1262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX = 4; 1272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1284755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private static final String COLOR_FULL_SELECTION = Colors.ACCOUNT_NAME + "=? AND " 1294755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + Colors.ACCOUNT_TYPE + "=? AND " + Colors.COLOR_TYPE + "=? AND " + Colors.COLOR_KEY 1304755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + "=?"; 1314755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan 1322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_NAME = Calendars.ACCOUNT_NAME; 1332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_TYPE = Calendars.ACCOUNT_TYPE; 1342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] ACCOUNT_PROJECTION = new String[] { 1352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_NAME, 1362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_TYPE, 1372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_NAME_INDEX = 0; 1392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_TYPE_INDEX = 1; 1402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // many tables have _id and event_id; pick a representative version to use as our generic 1421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_ID = Attendees._ID; 1431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_EVENT_ID = Attendees.EVENT_ID; 1441c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 1457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_ID, 1471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_EVENT_ID, 1487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 153646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 154646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 155646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 156646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 157646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 158646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 159646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 160646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 161646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 162646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 163646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 164646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 165646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 166646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 167646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 168646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 174ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 177f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik private CalendarInstancesHelper mInstancesHelper; 1789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1798ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 180f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // Due to an issue in Calendar Server restricting the length of the name we 181f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // had to strip it down 1828ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1838ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1848ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1858ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1868ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1873443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 188b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + ", " + 189b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTSTART_2445 + ", " + 190b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTEND_2445 + ", " + 1913443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1923443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 193b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 194b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1953443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 196b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 197b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " + 199b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 200c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik " SET " + Events.DIRTY + "=1" + 201b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 202b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_WHERE_CALENDAR_COLOR = Calendars.ACCOUNT_NAME + "=? AND " 204387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik + Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.CALENDAR_COLOR_KEY + "=?"; 2052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 2062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_WHERE_EVENT_COLOR = Events.ACCOUNT_NAME + "=? AND " 207387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik + Events.ACCOUNT_TYPE + "=? AND " + Events.EVENT_COLOR_KEY + "=?"; 2082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 20924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden protected static final String SQL_WHERE_ID = GENERIC_ID + "=?"; 21024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private static final String SQL_WHERE_EVENT_ID = GENERIC_EVENT_ID + "=?"; 2114d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?"; 2124d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID = Events.ORIGINAL_ID + 2134d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden "=? AND " + Events._SYNC_ID + " IS NULL"; 214ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 215ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan private static final String SQL_WHERE_ATTENDEE_BASE = 216ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID 217ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan + " AND " + 218ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 219ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 220b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 221ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + SQL_WHERE_ATTENDEE_BASE; 222b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 223b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 224b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 225ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + 226ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan " AND " + 227ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 228b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 229b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 2302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 231b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 232b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 233b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 2342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 235b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 236b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 237b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 238b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 239b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 240b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik Tables.EXTENDED_PROPERTIES + "." + CalendarContract.ExtendedProperties._ID + "=?"; 241b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 242b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 2432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik " WHERE " + Calendars.ACCOUNT_NAME + "=? AND " + 2442ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + "=?"; 245b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_DELETE_FROM_COLORS = "DELETE FROM " + Tables.COLORS + " WHERE " 2472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?"; 2482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 249fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 250fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 251fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 26181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 26281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 26381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 26481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 265b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 26681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 267b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 26881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 26918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 27018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 27118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 27218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 27318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 274b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 27518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 276b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")" + ") LEFT OUTER JOIN " + 27718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 27818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 279b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Attendees.EVENT_ID + "=" + 28018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 281b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 28218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 283b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 284b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.START_DAY + "<=? AND " + 285b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END_DAY + ">=?"; 28681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 287b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 288b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.BEGIN + "<=? AND " + 289b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END + ">=?"; 2909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 2982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * The sort order is: events with an earlier start time occur first and if 2992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the start times are the same, then events with a later end time occur 3002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * first. The later end time is ordered first so that long-running events in 3012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the calendar views appear first. If the start and end times of two events 3022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * are the same then we sort alphabetically on the title. This isn't 3032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * required for correctness, it just adds a nice touch. 3042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 3052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; 3062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 3072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 3082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * A regex for describing how we split search queries into tokens. Keeps 3092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * quoted phrases as one token. "one \"two three\"" ==> ["one" "two three"] 310dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 311dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 312dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 313dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 314dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 315dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 316dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 317dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 318dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 319dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 32081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 321dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 322dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 323dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 324dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 325dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 326dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 327dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 328dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 329dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 33081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 33118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 33218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 33318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 33418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 33518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 336b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_EMAIL + ")"; 33718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 33818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 33918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 34018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 34118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 34218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 343b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_NAME + ")"; 34418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 34581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 346b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.TITLE, 347b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.DESCRIPTION, 348b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.EVENT_LOCATION, 34918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 35018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 35181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 35281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 353a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 354a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 355a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 356a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 357a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 358a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 359a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 360a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 361a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 362a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 363a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 364a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 365dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 366dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 367dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 368dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 369dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 3708d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private static final HashSet<String> ALLOWED_URI_PARAMETERS = Sets.newHashSet( 3718d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.CALLER_IS_SYNCADAPTER, 3728d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.EventsEntity.ACCOUNT_NAME, 3738d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.EventsEntity.ACCOUNT_TYPE); 3748d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 375bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Set of columns allowed to be altered when creating an exception to a recurring event. */ 376bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final HashSet<String> ALLOWED_IN_EXCEPTION = new HashSet<String>(); 377bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden static { 378bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // _id, _sync_account, _sync_account_type, dirty, _sync_mark, calendar_id 379bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events._SYNC_ID); 380bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA1); 381bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA7); 38202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA3); 383bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.TITLE); 384bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION); 385bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION); 3862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR); 387387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR_KEY); 388bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.STATUS); 389c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS); 39002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6); 391bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DTSTART); 392c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // dtend -- set from duration as part of creating the exception 393bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_TIMEZONE); 394bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_END_TIMEZONE); 395bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DURATION); 396bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ALL_DAY); 397bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ACCESS_LEVEL); 398bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.AVAILABILITY); 399bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ALARM); 400bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_EXTENDED_PROPERTIES); 401bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RRULE); 402bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RDATE); 403bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXRULE); 404bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXDATE); 405bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_SYNC_ID); 406bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_INSTANCE_TIME); 407bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // originalAllDay, lastDate 408bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ATTENDEE_DATA); 409bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_MODIFY); 410bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_INVITE_OTHERS); 411bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_SEE_GUESTS); 412bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORGANIZER); 413c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan ALLOWED_IN_EXCEPTION.add(Events.CUSTOM_APP_PACKAGE); 414c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan ALLOWED_IN_EXCEPTION.add(Events.CUSTOM_APP_URI); 415bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // deleted, original_id, alerts 416bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 417bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 418bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Don't clone these from the base event into the exception event. */ 419bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final String[] DONT_CLONE_INTO_EXCEPTION = { 420bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events._SYNC_ID, 421bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA1, 42202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA2, 42302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA3, 42402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA4, 42502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA5, 42602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA6, 427bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA7, 42802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA8, 429c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA9, 430c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA10, 431bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden }; 432bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 433bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** set to 'true' to enable debug logging for recurrence exception code */ 434bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final boolean DEBUG_EXCEPTION = false; 435bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 436dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 437e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 438e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 4398bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 4408bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 441420b7fb569773ae573fbe90c3a9c522d4c368863Erik @VisibleForTesting 442420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected CalendarAlarmManager mCalendarAlarm; 443a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 444a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 445a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 446a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 447dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 448a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 449a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 450a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 451dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 452dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 453dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 454dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 455dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 456a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 457a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 458a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 459a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 473420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 477420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 479420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4908bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4918bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4928bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4938bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 494e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 495e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 496e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 497e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 498e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 499e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 500e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 5018bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 5028bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 5059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 506ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 507ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 508ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 509f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 510f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 511f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 512ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 513ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 514ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 516ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 5178bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 5188bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 519dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 520e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 521e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 522ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 523ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 5249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5254caf8d015918f619a67d321a152f150a01022717Andy McFadden mMetaData = new MetaData(mDbHelper); 5264caf8d015918f619a67d321a152f150a01022717Andy McFadden mInstancesHelper = new CalendarInstancesHelper(mDbHelper, mMetaData); 5274caf8d015918f619a67d321a152f150a01022717Andy McFadden 5289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 5299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 5309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 5329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 5339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 5369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 5379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 5389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 539e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 541ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 542ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 543420b7fb569773ae573fbe90c3a9c522d4c368863Erik // This is pulled out for testing 544420b7fb569773ae573fbe90c3a9c522d4c368863Erik initCalendarAlarm(); 545e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 546e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 5478bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 5499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 551420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected void initCalendarAlarm() { 552420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = getOrCreateCalendarAlarmManager(); 553420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.getScheduleNextAlarmWakeLock(); 554e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 555e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 556e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 557420b7fb569773ae573fbe90c3a9c522d4c368863Erik if (mCalendarAlarm == null) { 558420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = new CalendarAlarmManager(mContext); 559e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 560420b7fb569773ae573fbe90c3a9c522d4c368863Erik return mCalendarAlarm; 561e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 562e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 563ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 564ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 565ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 566ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 567ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 568ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 569ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 570ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 571ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 572ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 573ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 574ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 575c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan try { 576c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan doUpdateTimezoneDependentFields(); 577c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan } catch (IllegalStateException e) { 578c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // Added this because tests would fail if the provider is 579c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // closed by the time this is executed 580c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan 581c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // Nothing actionable here anyways. 582c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan } 583ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 584ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 585ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 58664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void verifyAccounts() { 58764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 58864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(AccountManager.get(getContext()).getAccounts()); 58964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 59064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 59164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 5929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 595315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 5969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 5989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 6009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 6039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 6049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 6059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 606ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 6079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 611315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 612315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 613315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 614315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 615315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 616315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 617315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 618315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 6199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 6209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 6219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 622315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 623ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 624315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 625315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 626a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 627315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 628315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 629315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 630ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 631315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 632315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 633ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 634315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 635ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 636ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 637ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 638ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 639420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 640ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 641ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 642f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 643f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 644f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 645ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 646ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 647ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 648ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 649ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 650f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 651f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 652f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 653ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 6549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 655ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 656ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 657315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 6603443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 661ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 662315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 663ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 664ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 665ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 666ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 667ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 668ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 669ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 6703443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 6713443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 672ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 673ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 674ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 675ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 676ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6773443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 678f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 679f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 680f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 681f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 682f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 683f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 684f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 685ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6863443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 687ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 688ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 690ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 691ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 692ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 693ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 694ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 695ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 696ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 697ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 698f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 699f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 700f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 701ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 702ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 703ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 704ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 705ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 706ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 707f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 708f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 709f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 710ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 711ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 712ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 713ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 714ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 715ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 716ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 717ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 718ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 719b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 720b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 721ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 722b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 723dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 724ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 725ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 726ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 727ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 728ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 729ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 730ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 731ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 732ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 733ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 734ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 735f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 736f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 737f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 738ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 739ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 7409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 741ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 742ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 743ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 744ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 745315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 746315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 747ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 748ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 749ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 750ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 751ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 75225e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 753ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 754315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 755315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 756ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 757ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 758f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 759f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 760f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 761ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 762ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 763ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 764315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 765315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 766315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 767315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 768315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 769ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 7709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 7719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 7729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 7739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 774315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 775315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7811f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7841f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7851f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7861f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7871f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7881f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7891f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 7902ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* selection */, null, 7912ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* sort */, 792d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 793315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 7942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik instancesTimezone, isHomeTimezone()); 7951f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 7961f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 7971f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 7981f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7991f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 8009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 801420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 806b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 809b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik mContentResolver.notifyChange(CalendarContract.CONTENT_URI, null, syncToNetwork); 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 8139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 8149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 815ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 816ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 8179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8188d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 8199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 8209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 8229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 8239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 824315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 8289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 829fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 8309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 831fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden case SYNCSTATE_ID: 832fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String selectionWithId = (SyncState._ID + "=?") 833fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden + (selection == null ? "" : " AND (" + selection + ")"); 834fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden // Prepend id to selectionArgs 835fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs = insertSelectionArg(selectionArgs, 836fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String.valueOf(ContentUris.parseId(uri))); 837fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selectionWithId, 838fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs, sortOrder); 8399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 8411ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 8438d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8448d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8459ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 8469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 8481ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 850636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 851b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 85319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 85419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 85519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 85619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 8578d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8588d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8599ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 86019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 86119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 86219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 86319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 864636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 865b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 86619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 86719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 8682f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 8692f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setTables(Tables.COLORS); 8702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setProjectionMap(sColorsProjectionMap); 8718d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8728d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8732f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 8742f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 87643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 877b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 8788d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8798d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 88243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 883b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 884636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 885b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 8889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 8899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 8909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 8919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 8939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 8959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 9019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 903315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 9042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceQuery(qb, begin, end, projection, selection, selectionArgs, 9052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik sortOrder, match == INSTANCES_BY_DAY, false /* don't force an expansion */, 906315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 90781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 90881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 90981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 91081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 91181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 91281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 91381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 91481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 91581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 91681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 91781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 91881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 91981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 92081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 921315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 92281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 92381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 9242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceSearchQuery(qb, begin, end, query, projection, selection, 9252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 926315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9276db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 9329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 9349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 9359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 9389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 9409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 942315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 943315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 944315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 94602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 948ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan qb.appendWhere(SQL_WHERE_ATTENDEE_BASE); 9499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 95102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 953636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 954b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 957b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 9589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 96002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 962636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 963b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 966b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 968b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 971b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 973b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 977b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 979636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 980b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 983b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 9849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 986b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 987636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 988b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 9899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 990315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 991b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 992315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 993315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 10009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10028d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private void validateUriParameters(Set<String> queryParameterNames) { 10038d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert final Set<String> parameterNames = queryParameterNames; 10048d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert for (String parameterName : parameterNames) { 10058d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert if (!ALLOWED_URI_PARAMETERS.contains(parameterName)) { 10068d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert throw new IllegalArgumentException("Invalid URI parameter: " + parameterName); 10078d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10088d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10098d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10108d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 10139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 1014ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 101539c65e5716e21e863d8de587d139dae85f99422fFred Quintana if (projection != null && projection.length == 1 101639c65e5716e21e863d8de587d139dae85f99422fFred Quintana && BaseColumns._COUNT.equals(projection[0])) { 101739c65e5716e21e863d8de587d139dae85f99422fFred Quintana qb.setProjectionMap(sCountProjectionMap); 101839c65e5716e21e863d8de587d139dae85f99422fFred Quintana } 101939c65e5716e21e863d8de587d139dae85f99422fFred Quintana 1020ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 1021ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 1022ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 1023ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 1024ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 1025ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 1026ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 1027ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 10289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 10299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 10309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 10319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 1032b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik c.setNotificationUri(mContentResolver, CalendarContract.Events.CONTENT_URI); 10339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 10359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 10409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 10419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 10429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 10439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 10449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 10459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 10469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 10479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 1048d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1049315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1050315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 10519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 10529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 10542ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik long rangeEnd, String[] projection, String selection, String[] selectionArgs, 10552ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String sort, boolean searchByDay, boolean forceExpansion, 10562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 10579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 105881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 10599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 10609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 10619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 10629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 1063315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 10649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 10659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 10669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 10679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 10689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1069315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 1070315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1071b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 10729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 10739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1074315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 1075315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1076b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 10779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10782ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10792ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = new String[] {String.valueOf(rangeEnd), 10808335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 10812ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 10822ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 10832ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 10842ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 10852ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 10862ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 10872ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10888335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 10897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 10909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 109281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 10932ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * Combine a set of arrays in the order they are passed in. All arrays must 10942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * be of the same type. 10952ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 10962ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static <T> T[] combine(T[]... arrays) { 10972ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (arrays.length == 0) { 10982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik throw new IllegalArgumentException("Must supply at least 1 array to combine"); 10992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int totalSize = 0; 11022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 11032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize += array.length; 11042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik T[] finalArray = (T[]) (Array.newInstance(arrays[0].getClass().getComponentType(), 11072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize)); 11082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int currentPos = 0; 11102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 11112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int length = array.length; 11122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik System.arraycopy(array, 0, finalArray, currentPos, length); 11132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik currentPos += array.length; 11142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return finalArray; 11162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 1119dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1120dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1121dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1122dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1123dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1124dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1125dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1126dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1127dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1128dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1129dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 113081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1131dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1132dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 113381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 113481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 113581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 113681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 113781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 113881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1139dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1140dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1141dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1142dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1143dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1144dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1145dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1146dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1147dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1148dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1149dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1150dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1151dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1152dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 115381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 115481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 115681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 115781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 115881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 115981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 116081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 116181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 116281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 116381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 116481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 116581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 116681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 116781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 116881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 116981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 117081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 117181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 1172cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * approximation of full-text search across multiple columns. The set 1173cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * of columns is specified by the SEARCH_COLUMNS constant. 1174cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * <p> 1175cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * Note the "WHERE" token isn't part of the returned string. The value 1176cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * may be passed into a query as the "HAVING" clause. 117781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 117881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 117981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 118081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 118181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 118281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 118381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 118481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 118581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 118681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 118781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 118881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1189dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1190dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1191dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 119281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 119381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 119481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 119718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 119818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 119918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 120081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 120181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 120281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 120381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 120481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 120581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 120618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 120718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 120881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 120918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 121018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 121118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 121281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1213f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1214f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 121518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 121681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 121781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 121881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 121981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 122081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 122181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 122281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 12232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selection, String[] selectionArgs, String sort, boolean searchByDay, 12242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 122518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 122681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 122781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1228dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 12292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 12302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 12312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 12322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 12332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 12342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 12352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 12362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 123718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 123818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1239dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1240dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 124181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 124281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 124381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1244315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 124581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 124681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 124781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 124881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 124981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 125018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 125118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 125256292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 125356292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1254315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1255315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1256315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 125756292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1258b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 125981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 126081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 126118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 126218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 126356292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 126456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1265315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1266315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1267315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 126856292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1269b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 127081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 127181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 127218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 1273c3780839fd044b5d8109860b57a199a2da1d804fMichael Chan Tables.INSTANCES + "." + Instances._ID /* groupBy */, 1274cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden searchWhere /* having */, sort); 127581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 127681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 12776db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1278315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1279315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 128081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 12816db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 128243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 128343556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1285192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 128643556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 128743556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1288192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 128943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1290315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1291315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1292b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 12938335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 12948335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 12958335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 12966db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 13019ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * table. Acquires the database lock and calls 13029ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #acquireInstanceRangeLocked(long, long, boolean, boolean, String, boolean)}. 13039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1307d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1308315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1309315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1311d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1312315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1313315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 13149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 13159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1316315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1317315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 13189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 13199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1331315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1332315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1333315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1335420b7fb569773ae573fbe90c3a9c522d4c368863Erik void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1336315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 13399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1340d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1341d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "acquireInstanceRange begin=" + begin + " end=" + end + 1342d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " useMin=" + useMinimumExpansionWindow + " force=" + forceExpansion); 1343d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1344d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1345315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1346315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1347315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1348315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1349315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 13529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 13549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 13569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 13579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 13589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 13599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 13649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1368315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1369315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1370315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1371315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1372315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1373315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1374315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 13757be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 13767be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 13777be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 13787be45683e367bd6897daf6444b03be938f8f5eaaErik } 1379315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1380315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1381315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1382d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 1383d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1384d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "Wiping instances and expanding from scratch"); 1385d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1386d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 13879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1388b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1389f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13906db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 13919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 13929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1393f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1394315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1395315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1397315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 13987be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 13997be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 14007be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 14017be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 14027be45683e367bd6897daf6444b03be938f8f5eaaErik 14037be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1404315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 14057be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 14067be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 14077be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 14087be45683e367bd6897daf6444b03be938f8f5eaaErik } 1409315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 14179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 14229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1425d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1426d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "instances are already expanded"); 1427d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1428f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 14299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 14309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 14319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1438f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1445f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 14479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1450315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 14519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 14549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 14559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 14569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 14609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 14619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 14629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 14639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 14649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 14689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 14736db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 147548587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 147648587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1477315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1478315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 14809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 14819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1484b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /** 1485b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Determines if the event is recurrent, based on the provided values. 1486b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1487b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden public static boolean isRecurrenceEvent(String rrule, String rdate, String originalId, 1488b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId) { 1489b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return (!TextUtils.isEmpty(rrule) || 1490b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(rdate) || 1491b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalId) || 1492b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalSyncId)); 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1495646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1496646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1497d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 1498646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1499d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * corrects the fields DTSTART, DTEND, and DURATION if necessary. 1500646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1501d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values The values to check and correct 1502d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param modValues Any updates will be stored here. This may be the same object as 1503d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <strong>values</strong>. 1504646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1505646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1506d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean fixAllDayTime(ContentValues values, ContentValues modValues) { 1507499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden Integer allDayObj = values.getAsInteger(Events.ALL_DAY); 1508499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden if (allDayObj == null || allDayObj == 0) { 1509d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return false; 1510d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1511d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1512646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1513646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1514d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtstart = values.getAsLong(Events.DTSTART); 1515d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtend = values.getAsLong(Events.DTEND); 1516d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String duration = values.getAsString(Events.DURATION); 1517d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Time time = new Time(); 1518d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String tempValue; 1519d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1520d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Change dtstart so h,m,s are 0 if necessary. 1521d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.clear(Time.TIMEZONE_UTC); 1522d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtstart.longValue()); 1523d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1524d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.hour = 0; 1525d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.minute = 0; 1526d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.second = 0; 1527d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTSTART, time.toMillis(true)); 1528d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1529d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1530d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1531d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If dtend exists for this event make sure it's h,m,s are 0. 1532d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (dtend != null) { 1533646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1534d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtend.longValue()); 1535646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1536646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1537646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1538646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1539d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden dtend = time.toMillis(true); 1540d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTEND, dtend); 1541646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1542646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1543d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1544646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1545d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (duration != null) { 1546d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int len = duration.length(); 1547d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1548d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * in the seconds format, and if so converts it to days. 1549d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 1550d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (len == 0) { 1551d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = null; 1552d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else if (duration.charAt(0) == 'P' && 1553d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration.charAt(len - 1) == 'S') { 1554d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1555d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1556d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = "P" + days + "D"; 1557d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DURATION, duration); 1558d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1559646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1560646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1561d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1562646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1563646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1564646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1565bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1566bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1567bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Determines whether the strings in the set name columns that may be overridden 1568bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * when creating a recurring event exception. 1569bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1570bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This uses a white list because it screens out unknown columns and is a bit safer to 1571bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * maintain than a black list. 1572bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1573bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private void checkAllowedInException(Set<String> keys) { 1574bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : keys) { 1575bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!ALLOWED_IN_EXCEPTION.contains(str.intern())) { 1576bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions can't overwrite " + str); 1577bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1578bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1579bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1580bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1581bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 158232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Splits a recurrent event at a specified instance. This is useful when modifying "this 158332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * and all future events". 158432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 158532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence rule has a COUNT specified, we need to split that at the point of the 158632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * exception. If the exception is instance N (0-based), the original COUNT is reduced 158732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * to N, and the exception's COUNT is set to (COUNT - N). 158832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 158932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence doesn't have a COUNT, we need to update or introduce an UNTIL value, 159032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the original recurrence will end just before the exception instance. (Note 159132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * that UNTIL dates are inclusive.) 159232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 159332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * This should not be used to update the first instance ("update all events" action). 1594bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 159532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @param values The original event values; must include EVENT_TIMEZONE and DTSTART. 159632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * The RRULE value may be modified (with the expectation that this will propagate 159732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * into the exception event). 1598bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param endTimeMillis The time before which the event must end (i.e. the start time of the 1599bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event instance). 160032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @return Values to apply to the original event. 1601bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1602bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static ContentValues setRecurrenceEnd(ContentValues values, long endTimeMillis) { 160332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden boolean origAllDay = values.getAsBoolean(Events.ALL_DAY); 160432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden String origRrule = values.getAsString(Events.RRULE); 1605bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 160632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence origRecurrence = new EventRecurrence(); 160732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.parse(origRrule); 1608bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 160932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Get the start time of the first instance in the original recurrence. 161032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long startTimeMillis = values.getAsLong(Events.DTSTART); 1611bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time dtstart = new Time(); 1612bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = values.getAsString(Events.EVENT_TIMEZONE); 161332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.set(startTimeMillis); 1614bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1615bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues updateValues = new ContentValues(); 161632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 161732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origRecurrence.count > 0) { 161832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden /* 161932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Generate the full set of instances for this recurrence, from the first to the 162032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * one just before endTimeMillis. The list should never be empty, because this method 162132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * should not be called for the first instance. All we're really interested in is 162232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * the *number* of instances found. 162332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden */ 162432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceSet recurSet = new RecurrenceSet(values); 162532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceProcessor recurProc = new RecurrenceProcessor(); 162632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long[] recurrences; 162732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden try { 162832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden recurrences = recurProc.expand(dtstart, recurSet, startTimeMillis, endTimeMillis); 162932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } catch (DateException de) { 163032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException(de); 163132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 163232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 163332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (recurrences.length == 0) { 163432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException("can't use this method on first instance"); 163532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 163632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 163732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence excepRecurrence = new EventRecurrence(); 16381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden excepRecurrence.parse(origRrule); // TODO: add/use a copy constructor to EventRecurrence 163932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden excepRecurrence.count -= recurrences.length; 164032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden values.put(Events.RRULE, excepRecurrence.toString()); 164132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 164232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.count = recurrences.length; 164332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 164432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } else { 164532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden Time untilTime = new Time(); 164632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 164732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // The "until" time must be in UTC time in order for Google calendar 164832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // to display it properly. For all-day events, the "until" time string 164932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // must include just the date field, and not the time field. The 165032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // repeating events repeat up to and including the "until" time. 165132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.timezone = Time.TIMEZONE_UTC; 165232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 165332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Subtract one second from the exception begin time to get the "until" time. 165432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.set(endTimeMillis - 1000); // subtract one second (1000 millis) 165532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origAllDay) { 165632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.hour = untilTime.minute = untilTime.second = 0; 165732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.allDay = true; 165832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.normalize(false); 165932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 166032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // This should no longer be necessary -- DTSTART should already be in the correct 166132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // format for an all-day event. 166232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.hour = dtstart.minute = dtstart.second = 0; 166332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.allDay = true; 166432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.timezone = Time.TIMEZONE_UTC; 166532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 166632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.until = untilTime.format2445(); 166732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 166832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 166932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden updateValues.put(Events.RRULE, origRecurrence.toString()); 1670bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.DTSTART, dtstart.normalize(true)); 1671bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return updateValues; 1672bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1673bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1674bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1675bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Handles insertion of an exception to a recurring event. 1676bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1677bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * There are two modes, selected based on the presence of "rrule" in modValues: 1678bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <ol> 1679bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Create a single instance exception ("modify current event only"). 1680bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Cap the original event, and create a new recurring event ("modify this and all 1681bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * future events"). 1682bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * </ol> 1683bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This may be used for "modify all instances of the event" by simply selecting the 1684bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * very first instance as the exception target. In that case, the ID of the "new" 1685bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event will be the same as the originalEventId. 1686bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1687bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param originalEventId The _id of the event to be modified 1688bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param modValues Event columns to update 1689c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * @param callerIsSyncAdapter Set if the content provider client is the sync adapter 1690bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return the ID of the new "exception" event, or -1 on failure 1691bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1692c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden private long handleInsertException(long originalEventId, ContentValues modValues, 1693c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden boolean callerIsSyncAdapter) { 1694bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1695bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.i(TAG, "RE: values: " + modValues.toString()); 1696bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1697bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1698bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Make sure they have specified an instance via originalInstanceTime. 1699bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Long originalInstanceTime = modValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1700bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime == null) { 1701bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions must specify " + 1702bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.ORIGINAL_INSTANCE_TIME); 1703bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1704bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1705bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Check for attempts to override values that shouldn't be touched. 1706bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden checkAllowedInException(modValues.keySet()); 1707bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1708c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // If this isn't the sync adapter, set the "dirty" flag in any Event we modify. 1709c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!callerIsSyncAdapter) { 1710c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.put(Events.DIRTY, true); 1711c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1712c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1713bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Wrap all database accesses in a transaction. 1714bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.beginTransaction(); 1715bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Cursor cursor = null; 1716bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1717bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that there's an instance corresponding to the specified time 1718bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (does this matter? it's weird, but not fatal?) 1719bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1720bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Grab the full set of columns for this event. 1721bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor = mDb.query(Tables.EVENTS, null /* columns */, 1722bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(originalEventId) }, 1723bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1724bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor.getCount() != 1) { 1725bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event ID " + originalEventId + " lookup failed (count is " + 1726bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.getCount() + ")"); 1727bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1728bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1729bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden //DatabaseUtils.dumpCursor(cursor); 1730bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 17312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If there's a color index check that it's valid 1732387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_index = modValues.getAsString(Events.EVENT_COLOR_KEY); 17332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_index)) { 17342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int calIdCol = cursor.getColumnIndex(Events.CALENDAR_ID); 17352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calId = cursor.getLong(calIdCol); 17362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 17372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 17382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calId != null) { 17392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calId); 17402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 17412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 17422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 17432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_index, Colors.TYPE_EVENT); 17462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1748bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1749bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Verify that the original event is in fact a recurring event by checking for the 1750bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * presence of an RRULE. If it's there, we assume that the event is otherwise 1751bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * properly constructed (e.g. no DTEND). 1752bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1753bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.moveToFirst(); 1754bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int rruleCol = cursor.getColumnIndex(Events.RRULE); 1755bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (TextUtils.isEmpty(cursor.getString(rruleCol))) { 1756bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event has no rrule"); 1757bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1758bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1759bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1760bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: old RRULE is " + cursor.getString(rruleCol)); 1761bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1762bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1763bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Verify that the original event is not itself a (single-instance) exception. 1764bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int originalIdCol = cursor.getColumnIndex(Events.ORIGINAL_ID); 1765bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!TextUtils.isEmpty(cursor.getString(originalIdCol))) { 1766bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event is an exception"); 1767bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1768bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1769bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1770bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createSingleException = TextUtils.isEmpty(modValues.getAsString(Events.RRULE)); 1771bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1772bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: check for the presence of an existing exception on this event+instance? 1773bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The caller should be modifying that, not creating another exception. 1774bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Alternatively, we could do that for them.) 1775bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1776bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Create a new ContentValues for the new event. Start with the original event, 1777bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // and drop in the new caller-supplied values. This will set originalInstanceTime. 1778bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues values = new ContentValues(); 1779bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 1780f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor.close(); 1781f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor = null; 1782bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1783b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: if we're changing this to an all-day event, we should ensure that 1784b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // hours/mins/secs on DTSTART are zeroed out (before computing DTEND). 1785b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // See fixAllDayTime(). 1786b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1787bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createNewEvent = true; 1788bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createSingleException) { 1789bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1790bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Save a copy of a few fields that will migrate to new places. 1791bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1792bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _id = values.getAsString(Events._ID); 1793bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _sync_id = values.getAsString(Events._SYNC_ID); 1794bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1795bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1796bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Wipe out some fields that we don't want to clone into the exception event. 1798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : DONT_CLONE_INTO_EXCEPTION) { 1800bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(str); 1801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1803bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1804bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Merge the new values on top of the existing values. Note this sets 1805bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * originalInstanceTime. 1806bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1807bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1808bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1809bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1810bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Copy some fields to their "original" counterparts: 1811bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id --> original_id 1812bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _sync_id --> original_sync_id 1813bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * allDay --> originalAllDay 1814bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1815bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this event hasn't been sync'ed with the server yet, the _sync_id field will 1816bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * be null. We will need to fill original_sync_id in later. (May not be able to 1817bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * do it right when our own _sync_id field gets populated, because the order of 1818bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * events from the server may not be what we want -- could update the exception 1819bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * before updating the original event.) 1820bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1821bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id is removed later (right before we write the event). 1822bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1823bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ID, _id); 1824bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_SYNC_ID, _sync_id); 1825bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ALL_DAY, allDay); 1826bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1827bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Mark the exception event status as "tentative", unless the caller has some 1828bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // other value in mind (like STATUS_CANCELED). 1829bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!values.containsKey(Events.STATUS)) { 1830bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.STATUS, Events.STATUS_TENTATIVE); 1831bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1832bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1833bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // We're converting from recurring to non-recurring. Clear out RRULE and replace 1834bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DURATION with DTEND. 1835c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.remove(Events.RRULE); 1836bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1837bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Duration duration = new Duration(); 1838bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String durationStr = values.getAsString(Events.DURATION); 1839bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1840bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden duration.parse(durationStr); 1841bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } catch (Exception ex) { 1842bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // NullPointerException if the original event had no duration. 1843bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DateException if the duration was malformed. 1844bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Bad duration in recurring event: " + durationStr, ex); 1845bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1846bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1847bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1848c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1849c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * We want to compute DTEND as an offset from the start time of the instance. 1850c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If the caller specified a new value for DTSTART, we want to use that; if not, 1851c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the DTSTART in "values" will be the start time of the first instance in the 1852c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * recurrence, so we want to replace it with ORIGINAL_INSTANCE_TIME. 1853c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1854c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long start; 1855c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.DTSTART)) { 1856c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.DTSTART); 1857c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1858c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1859c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.put(Events.DTSTART, start); 1860c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1861bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.DTEND, start + duration.getMillis()); 1862bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1863c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "RE: ORIG_INST_TIME=" + start + 1864c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ", duration=" + duration.getMillis() + 1865bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ", generated DTEND=" + values.getAsLong(Events.DTEND)); 1866bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 186785c09a31bcc3a18e173428bf7b628cec2834bebcAndy McFadden values.remove(Events.DURATION); 1868bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1869bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1870bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're going to "split" the recurring event, making the old one stop before 1871bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * this instance, and creating a new recurring event that starts here. 1872bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1873bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * No need to fill out the "original" fields -- the new event is not tied to 1874bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * the previous event in any way. 1875bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1876bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this is the first event in the series, we can just update the existing 1877bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event with the values. 1878bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1879bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean canceling = (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 1880bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1881bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime.equals(values.getAsLong(Events.DTSTART))) { 1882bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1883bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Update fields in the existing event. Rather than use the merged data 1884bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * from the cursor, we just do the update with the new value set after 1885bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * removing the ORIGINAL_INSTANCE_TIME entry. 1886bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1887bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (canceling) { 1888bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: should we just call deleteEventInternal? 1889bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "Note: canceling entire event via exception call"); 1890bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1891bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1892bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: updating full event"); 1893bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1894ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(modValues)) { 1895ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 1896ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 1897ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 1898bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden modValues.remove(Events.ORIGINAL_INSTANCE_TIME); 1899bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 1900bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1901bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden createNewEvent = false; // skip event creation and related-table cloning 1902bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1903bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1904bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: splitting event"); 1905bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1906bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1907bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 190832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Cap the original event so it ends just before the target instance. In 190932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * some cases (nonzero COUNT) this will also update the RRULE in "values", 191032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the exception we're creating terminates appropriately. If a 191132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * new RRULE was specified by the caller, the new rule will overwrite our 191232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * changes when we merge the new values in below (which is the desired 191332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * behavior). 1914bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1915bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues splitValues = setRecurrenceEnd(values, originalInstanceTime); 1916bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, splitValues, SQL_WHERE_ID, 1917bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1918bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1919bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 192032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Prepare the new event. We remove originalInstanceTime, because we're now 1921bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * creating a new event rather than an exception. 1922bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1923bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're always cloning a non-exception event (we tested to make sure the 1924bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event doesn't specify original_id, and we don't allow original_id in the 1925bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * modValues), so we shouldn't end up creating a new event that looks like 1926bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * an exception. 1927bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1928bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1929bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events.ORIGINAL_INSTANCE_TIME); 1930bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1931c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1932bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1933bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long newEventId; 1934bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createNewEvent) { 1935bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events._ID); // don't try to set this explicitly 1936be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 1937be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, null); 1938be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 1939be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 1940be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 1941bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1942bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = mDb.insert(Tables.EVENTS, null, values); 1943bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (newEventId < 0) { 1944bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Unable to add exception to recurring event"); 1945bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Values: " + values); 1946bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1947bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1948bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1949bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: new ID is " + newEventId); 1950bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1951bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1952b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: do we need to do something like this? 1953b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden //updateEventRawTimesLocked(id, updatedValues); 1954b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1955b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1956b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Force re-computation of the Instances associated with the recurrence event. 1957b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1958b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, newEventId, true, mDb); 1959b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1960bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1961bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Some of the other tables (Attendees, Reminders, ExtendedProperties) reference 1962c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Event ID. We need to copy the entries from the old event, filling in the 1963c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * new event ID, so that somebody doing a SELECT on those tables will find 1964c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * matching entries. 1965bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1966bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden CalendarDatabaseHelper.copyEventRelatedTables(mDb, newEventId, originalEventId); 1967c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1968c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1969c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If we modified Event.selfAttendeeStatus, we need to keep the corresponding 1970c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * entry in the Attendees table in sync. 1971c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1972c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 1973c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1974c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * Each Attendee is identified by email address. To find the entry that 1975c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * corresponds to "self", we want to compare that address to the owner of 1976c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Calendar. We're expecting to find one matching entry in Attendees. 1977c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1978c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long calendarId = values.getAsLong(Events.CALENDAR_ID); 1979f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden String accountName = getOwner(calendarId); 1980f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden 1981f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden if (accountName != null) { 1982c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ContentValues attValues = new ContentValues(); 1983c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.put(Attendees.ATTENDEE_STATUS, 1984c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.getAsString(Events.SELF_ATTENDEE_STATUS)); 1985c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1986c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (DEBUG_EXCEPTION) { 1987c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "Updating attendee status for event=" + newEventId + 1988c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden " name=" + accountName + " to " + 1989c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.getAsString(Attendees.ATTENDEE_STATUS)); 1990c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1991c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden int count = mDb.update(Tables.ATTENDEES, attValues, 1992c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Attendees.EVENT_ID + "=? AND " + Attendees.ATTENDEE_EMAIL + "=?", 1993c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden new String[] { String.valueOf(newEventId), accountName }); 1994b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (count != 1 && count != 2) { 1995b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // We're only expecting one matching entry. We might briefly see 1996b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // two during a server sync. 19977148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik Log.e(TAG, "Attendee status update on event=" + newEventId 19987148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " touched " + count + " rows. Expected one or two rows."); 1999b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (false) { 2000b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This dumps PII in the log, don't ship with it enabled. 2001b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Cursor debugCursor = mDb.query(Tables.ATTENDEES, null, 2002b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.EVENT_ID + "=? AND " + 2003b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.ATTENDEE_EMAIL + "=?", 2004b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { String.valueOf(newEventId), accountName }, 2005b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden null, null, null); 2006b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden DatabaseUtils.dumpCursor(debugCursor); 20070332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan if (debugCursor != null) { 20080332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan debugCursor.close(); 20090332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan } 2010b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden } 2011b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden throw new RuntimeException("Status update WTF"); 2012c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2013c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2014c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2015bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 2016b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 2017b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Update any Instances changed by the update to this Event. 2018b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 2019b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, originalEventId, false, mDb); 2020bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = originalEventId; 2021bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2022bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 2023bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.setTransactionSuccessful(); 2024bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return newEventId; 2025bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } finally { 2026bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor != null) { 2027bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.close(); 2028bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2029bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.endTransaction(); 2030bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2031bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2032bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 2033222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden /** 2034222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Fills in the originalId column for previously-created exceptions to this event. If 2035222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this event is not recurring or does not have a _sync_id, this does nothing. 2036222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2037222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * The server might send exceptions before the event they refer to. When 2038222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this happens, the originalId field will not have been set in the 2039222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * exception events (it's the recurrence events' _id field, so it can't be 2040222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * known until the recurrence event is created). When we add a recurrence 2041222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * event with a non-empty _sync_id field, we write that event's _id to the 2042222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * originalId field of any events whose originalSyncId matches _sync_id. 2043222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2044222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Note _sync_id is only expected to be unique within a particular calendar. 2045222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * 2046222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param id The ID of the Event 2047222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param values Values for the Event being inserted 2048222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden */ 2049222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden private void backfillExceptionOriginalIds(long id, ContentValues values) { 2050222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String syncId = values.getAsString(Events._SYNC_ID); 2051222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rrule = values.getAsString(Events.RRULE); 2052222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rdate = values.getAsString(Events.RDATE); 2053222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String calendarId = values.getAsString(Events.CALENDAR_ID); 2054222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2055222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden if (TextUtils.isEmpty(syncId) || TextUtils.isEmpty(calendarId) || 2056222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden (TextUtils.isEmpty(rrule) && TextUtils.isEmpty(rdate))) { 2057222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden // Not a recurring event, or doesn't have a server-provided sync ID. 2058222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden return; 2059222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2060222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2061222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden ContentValues originalValues = new ContentValues(); 2062222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden originalValues.put(Events.ORIGINAL_ID, id); 2063222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden mDb.update(Tables.EVENTS, originalValues, 2064222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden Events.ORIGINAL_SYNC_ID + "=? AND " + Events.CALENDAR_ID + "=?", 2065222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden new String[] { syncId, calendarId }); 2066222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2067222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 20689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2069b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 2070ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 20719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 20729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20738d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 20740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 20750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_INSERT, uri, values, callerIsSyncAdapter, match, 20760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik null /* selection */, null /* selection args */); 20779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 2081bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case SYNCSTATE: 20829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 20839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 20857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2086c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 20877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 20898253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert if (values.containsKey(Events.ORIGINAL_SYNC_ID) 20908253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && values.containsKey(Events.ORIGINAL_INSTANCE_TIME) 20918253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && Events.STATUS_CANCELED == values.getAsInteger(Events.STATUS)) { 20928253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // event is a canceled instance of a recurring event, it doesn't these 20938253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // values but lets fake some to satisfy curious consumers. 20948253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert final long origStart = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 20958253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTSTART, origStart); 20968253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTEND, origStart); 20978253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.EVENT_TIMEZONE, Time.TIMEZONE_UTC); 20988253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } else { 20998253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert throw new RuntimeException("DTSTART field missing from event"); 21008253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } 21019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 2103e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 2104be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 2105be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(updatedValues, null); 2106be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 2107be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(updatedValues); 2108be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 2109e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 2110e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 21149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calendar_id = updatedValues.getAsLong(Events.CALENDAR_ID); 21162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendar_id == null) { 21172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // validateEventData checks this for non-sync adapter 21182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // inserts 21192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("New events must specify a calendar id"); 21202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color is valid if it is being set 2122387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = updatedValues.getAsString(Events.EVENT_COLOR_KEY); 21232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 21242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calendar_id); 21252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 21262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 21272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 21282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 21292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 21302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, color_id, 21322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_EVENT); 21332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik updatedValues.put(Events.EVENT_COLOR, color); 21342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 21362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!updatedValues.containsKey(Events.ORGANIZER)) { 21372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 21389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 21409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 21449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 214634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 214734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !updatedValues.containsKey(Events.ORIGINAL_ID)) { 214834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = getOriginalId(updatedValues 2149ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson .getAsString(Events.ORIGINAL_SYNC_ID), 2150ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson updatedValues.getAsString(Events.CALENDAR_ID)); 215134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId != -1) { 215234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_ID, originalId); 215334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 215434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } else if (!updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 215534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && updatedValues.containsKey(Events.ORIGINAL_ID)) { 215634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = getOriginalSyncId(updatedValues 215734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsLong(Events.ORIGINAL_ID)); 215834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (!TextUtils.isEmpty(originalSyncId)) { 215934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_SYNC_ID, originalSyncId); 216034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 216134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 2162d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(updatedValues, updatedValues)) { 2163f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2164f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 2165f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 2166f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2167646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 21681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updatedValues.remove(Events.HAS_ALARM); // should not be set by caller 2169c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 21709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 21719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 21729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 2173f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 2174f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik true /* new event */, mDb); 21759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 21779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 21789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 21799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 21809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 21812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 21829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 21849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2185b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 2186222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden backfillExceptionOriginalIds(id, values); 2187222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2188dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2191bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID: 2192bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long originalEventId = ContentUris.parseId(uri); 2193c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden id = handleInsertException(originalEventId, values, callerIsSyncAdapter); 2194bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden break; 21959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 219682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden // TODO: verify that all required fields are present 21979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 21989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 2199c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 22009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 2201c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 22029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 2203fa332ecedc0c340109811552407142f6e4f600b2RoboErik String eventsUrl = values.getAsString(Calendars.CAL_SYNC1); 22041b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 22059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2206387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String cal_color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 22072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(cal_color_id)) { 22082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 22092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 22102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, cal_color_id, 22112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_CALENDAR); 22122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Calendars.CALENDAR_COLOR, color); 22132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2215dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 22169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 22182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // verifyTransactionAllowed requires this be from a sync 22192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter, all of the required fields are marked NOT NULL in 22202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // the db. TODO Do we need explicit checks here or should we 22212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // just let sqlite throw if something isn't specified? 22222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = uri.getQueryParameter(Colors.ACCOUNT_NAME); 22232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = uri.getQueryParameter(Colors.ACCOUNT_TYPE); 2224387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String colorIndex = values.getAsString(Colors.COLOR_KEY); 22252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 22262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Account name and type must be non" 22272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " empty parameters for " + uri); 22282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(colorIndex)) { 22302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("COLOR_INDEX must be non empty for " + uri); 22312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!values.containsKey(Colors.COLOR_TYPE) || !values.containsKey(Colors.COLOR)) { 22332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException( 22342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik "New colors must contain COLOR_TYPE and COLOR"); 22352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Make sure the account we're inserting for is the same one the 22372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter is claiming to be. TODO should we throw if they 22382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // aren't the same? 22392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_NAME, accountName); 22402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_TYPE, accountType); 22412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 22422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color doesn't already exist 22432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 22442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 22454755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan final long colorType = values.getAsLong(Colors.COLOR_TYPE); 22464755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 22472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.getCount() != 0) { 22484755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("color type " + colorType 22494755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + " and index " + colorIndex 22507148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " already exists for account and type provided"); 22512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 22532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) 22542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 22552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik id = mDbHelper.colorsInsert(values); 22572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 22589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 22599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 22609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 22619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 22629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 22649ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Attendees.EVENT_ID); 22659ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 22669ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 22677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22689ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.attendeesInsert(values); 22699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 22741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden { 22751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 22761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventIdObj == null) { 22779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 22781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "contain a numeric event_id"); 22799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 22811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventIdObj); 22821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setEventDirty(eventIdObj); 22837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22849ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.remindersInsert(values); 22859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // We know this event has at least one reminder, so make sure "hasAlarm" is 1. 22871c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setHasAlarm(eventIdObj, 1); 22881c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2293420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 22969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 22979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 22999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 23022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 23032fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 2306b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (!values.containsKey(CalendarContract.ExtendedProperties.EVENT_ID)) { 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 23099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2311b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik final Long eventId = values 2312b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik .getAsLong(CalendarContract.ExtendedProperties.EVENT_ID); 23139ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 23149ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 23157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.extendedPropertiesInsert(values); 23179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 23183b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden case EMMA: 23193b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Special target used during code-coverage evaluation. 23203b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden handleEmmaRequest(values); 23213b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden break; 23229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 23239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 23249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 23269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 23279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 23286db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2329315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 23307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 23339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 23369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 23379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 23409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2342e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 23433b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Handles special commands related to EMMA code-coverage testing. 23443b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 23453b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * @param values Parameters from the caller. 23463b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 23473b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static void handleEmmaRequest(ContentValues values) { 23483b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /* 23493b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * This is not part of the public API, so we can't share constants with the CTS 23503b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * test code. 23513b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 23523b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Bad requests, or attempting to request EMMA coverage data when the coverage libs 23533b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * aren't linked in, will cause an exception. 23543b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 23553b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String cmd = values.getAsString("cmd"); 23563b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden if (cmd.equals("start")) { 23573b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // We'd like to reset the coverage data, but according to FAQ item 3.14 at 23583b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // http://emma.sourceforge.net/faq.html, this isn't possible in 2.0. 23593b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage testing started"); 23603b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } else if (cmd.equals("stop")) { 23613b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Call com.vladium.emma.rt.RT.dumpCoverageData() to cause a data dump. We 23623b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // may not have been built with EMMA, so we need to do this through reflection. 23633b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String filename = values.getAsString("outputFileName"); 23643b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23653b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden File coverageFile = new File(filename); 23663b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden try { 23673b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); 23683b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", 23693b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden coverageFile.getClass(), boolean.class, boolean.class); 23703b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23713b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden dumpCoverageMethod.invoke(null, coverageFile, false /*merge*/, 23723b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden false /*stopDataCollection*/); 23733b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage data written to " + filename); 23743b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } catch (Exception e) { 23753b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden throw new RuntimeException("Emma coverage dump failed", e); 23763b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23773b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23783b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23793b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23803b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /** 23815ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * Validates the recurrence rule, if any. We allow single- and multi-rule RRULEs. 2382ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * <p> 23835ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * TODO: Validate RDATE, EXRULE, EXDATE (possibly passing in an indication of whether we 23845ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * believe we have the full set, so we can reject EXRULE when not accompanied by RRULE). 2385ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * 2386ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * @return A boolean indicating successful validation. 2387ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden */ 2388ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden private boolean validateRecurrenceRule(ContentValues values) { 2389ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden String rrule = values.getAsString(Events.RRULE); 2390ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2391ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!TextUtils.isEmpty(rrule)) { 23925ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden String[] ruleList = rrule.split("\n"); 23935ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden for (String recur : ruleList) { 23945ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden EventRecurrence er = new EventRecurrence(); 23955ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden try { 23965ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden er.parse(recur); 23975ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } catch (EventRecurrence.InvalidFormatException ife) { 23985ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden Log.w(TAG, "Invalid recurrence rule: " + recur); 2399bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 24005ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden return false; 24015ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } 2402ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2403ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2404ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2405ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden return true; 2406ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2407ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2408bfea6da707f8d352432096371e7da76c230d9059Michael Chan private void dumpEventNoPII(ContentValues values) { 2409bfea6da707f8d352432096371e7da76c230d9059Michael Chan if (values == null) { 2410bfea6da707f8d352432096371e7da76c230d9059Michael Chan return; 2411bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2412bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2413bfea6da707f8d352432096371e7da76c230d9059Michael Chan StringBuilder bob = new StringBuilder(); 2414bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("dtStart: ").append(values.getAsLong(Events.DTSTART)); 2415bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndtEnd: ").append(values.getAsLong(Events.DTEND)); 2416bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nall_day: ").append(values.getAsInteger(Events.ALL_DAY)); 2417bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ntz: ").append(values.getAsString(Events.EVENT_TIMEZONE)); 2418bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndur: ").append(values.getAsString(Events.DURATION)); 2419bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrrule: ").append(values.getAsString(Events.RRULE)); 2420bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrdate: ").append(values.getAsString(Events.RDATE)); 2421bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nlast_date: ").append(values.getAsLong(Events.LAST_DATE)); 2422bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2423bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nid: ").append(values.getAsLong(Events._ID)); 2424bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nsync_id: ").append(values.getAsString(Events._SYNC_ID)); 2425bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_id: ").append(values.getAsLong(Events.ORIGINAL_ID)); 2426bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_sync_id: ").append(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2427bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_inst_time: ").append(values.getAsLong(Events.ORIGINAL_INSTANCE_TIME)); 2428bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_all_day: ").append(values.getAsInteger(Events.ORIGINAL_ALL_DAY)); 2429bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2430bfea6da707f8d352432096371e7da76c230d9059Michael Chan Log.i(TAG, bob.toString()); 2431bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2432bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2433ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden /** 243462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Do some scrubbing on event data before inserting or updating. In particular make 243562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * dtend, duration, etc make sense for the type of event (regular, recurrence, exception). 243662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Remove any unexpected fields. 2437e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 243862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param values the ContentValues to insert. 243962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param modValues if non-null, explicit null entries will be added here whenever something 244062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * is removed from <strong>values</strong>. 2441e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 244262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden private void scrubEventData(ContentValues values, ContentValues modValues) { 2443e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2444e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2445e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2446e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2447c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2448e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2449e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2450e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2451e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2452e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2453e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2454ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden // rrule is a valid recurrence rule 2455e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2456e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2457e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2458ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(values)) { 2459ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2460ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 2461ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2462e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 246362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DTEND, ORIGINAL_SYNC_ID, ORIGINAL_INSTANCE_TIME"); 2464e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 246562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence: " + values); 2466e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2467e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2468c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.remove(Events.ORIGINAL_SYNC_ID); 2469e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 247062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 247162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DTEND); 247262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_SYNC_ID); 247362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_INSTANCE_TIME); 247462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2475e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2476e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2477e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2478e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2479e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2480e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2481e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2482e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2483e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2484e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2485e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 248662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2487e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 248862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence exception: " + values); 2489e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2490e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 249162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 249262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 249362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2494e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2495e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2496e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2497e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2498e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2499e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2500e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2501e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2502e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2503e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2504e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 250562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2506e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 250762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for event: " + values); 2508e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2509e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 251062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 251162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 251262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2513e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2514e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2515e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2516e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 2517d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2518d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Validates event data. Pass in the full set of values for the event (i.e. not just 2519d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * a part that's being updated). 2520d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2521d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values Event data. 2522d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws IllegalArgumentException if bad data is found. 2523d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 2524d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private void validateEventData(ContentValues values) { 252582b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.CALENDAR_ID))) { 252682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include a calendar_id"); 252782b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 252882b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.EVENT_TIMEZONE))) { 252982b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include an eventTimezone"); 253082b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 253182b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden 2532d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtstart = values.getAsLong(Events.DTSTART) != null; 2533d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2534d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2535d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2536d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2537d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasRrule || hasRdate) { 2538d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!validateRecurrenceRule(values)) { 2539d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2540d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.getAsString(Events.RRULE)); 2541d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2542d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2543d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 2544d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDtstart) { 2545bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2546d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTSTART cannot be empty."); 2547d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2548d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDuration && !hasDtend) { 2549bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2550d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 2551d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "an event."); 2552d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2553d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasDuration && hasDtend) { 2554bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2555d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Cannot have both DTEND and DURATION in an event"); 2556d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2557d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2558d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 25599ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private void setEventDirty(long eventId) { 25609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Object[] {eventId}); 25617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 25627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 2563ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson private long getOriginalId(String originalSyncId, String calendarId) { 2564ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson if (TextUtils.isEmpty(originalSyncId) || TextUtils.isEmpty(calendarId)) { 256534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return -1; 256634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 256734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 256834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = -1; 256934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 257034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 257134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, 2572ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson Events._SYNC_ID + "=?" + " AND " + Events.CALENDAR_ID + "=?", 2573ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson new String[] {originalSyncId, calendarId}, null); 257434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 257534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalId = c.getLong(0); 257634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 257734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 257834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 257934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 258034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 258134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 258234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalId; 258334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 258434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 258534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private String getOriginalSyncId(long originalId) { 258634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId == -1) { 258734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return null; 258834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 258934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 259034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = null; 259134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 259234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 259334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, new String[] {Events._SYNC_ID}, 259434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._ID + "=?", new String[] {Long.toString(originalId)}, null); 259534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 259634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalSyncId = c.getString(0); 259734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 259834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 259934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 260034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 260134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 260234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 260334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalSyncId; 260434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 260534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 26064755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private Cursor getColorByTypeIndex(String accountName, String accountType, long colorType, 26074755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan String colorIndex) { 26084755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan return mDb.query(Tables.COLORS, COLORS_PROJECTION, COLOR_FULL_SELECTION, new String[] { 26094755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan accountName, accountType, Long.toString(colorType), colorIndex 26104755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan }, null, null, null); 26112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 26139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2614f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * Gets a calendar's "owner account", i.e. the e-mail address of the owner of the calendar. 2615f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * 2616f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * @param calId The calendar ID. 26179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 26189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2620f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2621f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2622f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2623f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2624f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2625f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 26269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 26279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 26289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 26299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 26319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 26329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 26339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 26349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2636f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2637f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2638f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 26489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private Account getAccount(long calId) { 26512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = null; 26522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor cursor = null; 26532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 26542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 26552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ACCOUNT_PROJECTION, null /* selection */, null /* selectionArgs */, 26562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* sort */); 26572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor == null || !cursor.moveToFirst()) { 26582f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (Log.isLoggable(TAG, Log.DEBUG)) { 26592f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 26602f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26612f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return null; 26622f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26632f251c778c06d21ed7693a70f4a1268ff929242eRoboErik account = new Account(cursor.getString(ACCOUNT_NAME_INDEX), 26642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.getString(ACCOUNT_TYPE_INDEX)); 26652f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 26662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) { 26672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 26682f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26692f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return account; 26712f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26722f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 26739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 26759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 26769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 26789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 26799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 26839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 26849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 26859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 26869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 26879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 26899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 26909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 26919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 26929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 27019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 270324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param attendeeValues the column values for one row in the Attendees table. 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 270724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Long eventIdObj = attendeeValues.getAsLong(Attendees.EVENT_ID); 270824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (eventIdObj == null) { 270924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.w(TAG, "Attendee update values don't include an event_id"); 271024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return; 271124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 271224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = eventIdObj; 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 27159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2725f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2726f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2727f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 27319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 27339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 27439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 27449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 27459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 27469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2747f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2748f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2749f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 27509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 27539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 27609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 27659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 27669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 27679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 27719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 27729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 27739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 277724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Select a default value for "status" based on the relationship. 27789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 277924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer relationObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 278024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (relationObj != null) { 278124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int rel = relationObj; 27829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 27839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 27849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 278724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // If the status is specified, use that. 278824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer statusObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 278924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (statusObj != null) { 279024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden status = statusObj; 27919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 27949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2795b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2796b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 27979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2799d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 28001c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Set the "hasAlarm" column in the database. 28011c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 28021c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param eventId The _id of the Event to update. 28031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param val The value to set it to (0 or 1). 28041c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 28051c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private void setHasAlarm(long eventId, int val) { 28061c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues values = new ContentValues(); 28071c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden values.put(Events.HAS_ALARM, val); 28081c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int count = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 28091c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 28101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (count != 1) { 28111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Log.w(TAG, "setHasAlarm on event " + eventId + " updated " + count + 28121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden " rows (expected 1)"); 28131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 28141c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 28151c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 28161c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 2817d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Calculates the "last date" of the event. For a regular event this is the start time 2818d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * plus the duration. For a recurring event this is the start date of the last event in 2819d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence, plus the duration. The event recurs forever, this returns -1. If 2820d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence rule can't be parsed, this returns -1. 2821d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2822d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values 2823d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the date, in milliseconds, since the start of the epoch (UTC), or -1 if an 2824d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * exceptional condition exists. 2825d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws DateException 2826d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 28279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 28289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 28299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 28309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 28319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 28329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 28339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 28349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 28359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 28369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 28379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 28419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 28439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 28449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 28489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 28499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 28509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 28519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 28529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 28539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 28549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 28559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 28569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 28579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 28589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2861f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2862f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2863f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2864f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2865f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2866f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2867b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik values.get(CalendarContract.Events.RRULE), e); 2868f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2869d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: this should throw an exception or return a distinct error code 2870f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2871f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 28729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2873f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 28749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 28759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 28769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 28789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 28809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 28819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 28829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 28849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 28869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 28889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 28899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 2890d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // repeats forever 28919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 28929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 28949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 28959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 28969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 28999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 29009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2904e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2905e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 290682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * @param values the ContentValues (in/out); must include DTSTART and, if the event is 290782b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * recurring, the columns necessary to process a recurrence rule (RRULE, DURATION, 290882b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * EVENT_TIMEZONE, etc). 2909e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2910e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2911e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 29129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 29139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 29149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 29159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 29169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 29199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 29209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2921f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2922f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2923f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 29249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 29259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2928d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2929d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Creates or updates an entry in the EventsRawTimes table. 2930d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2931d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param eventId The ID of the event that was just created or is being updated. 2932d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values For a new event, the full set of event values; for an updated event, 2933d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the set of values that are being changed. 2934d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 29359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 29369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 29379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2938b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.EVENT_ID, eventId); 29399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 29419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 29439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 29449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 29459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 29469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 29499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 29509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 29519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 29549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 29559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 29569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 29579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 2958b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTSTART_2445, time.format2445()); 29599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 29629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 29639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 2964b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTEND_2445, time.format2445()); 29659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 29689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 29709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 29719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 29729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 29739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 29749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 29759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 2977b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.ORIGINAL_INSTANCE_TIME_2445, 2978b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 29799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 29829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 29839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 29849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 2985b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.LAST_DATE_2445, time.format2445()); 29869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 29899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2992b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2993b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2994ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 29959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 29969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29978d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 29989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 29990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_DELETE, uri, null, callerIsSyncAdapter, match, 30000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 30010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 30029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 30039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 30049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 30059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 30072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 30089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 30099323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3010dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3011dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3012dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 3013dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 30149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 30168d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return deleteMatchingColors(appendAccountToSelection(uri, selection, 30178d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE), 30182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 30192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 30201ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 30219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30227e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 30238d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection( 30248d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert uri, selection, Events.ACCOUNT_NAME, Events.ACCOUNT_TYPE); 30257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 30261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 3027ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION, 30281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 30297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 30309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 30311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 30321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 303310b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 30349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3035420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3036dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 30379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 30389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 30399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 30409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 30429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30431ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 30441ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 30451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 304610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 30471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 3048bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID2: 3049bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden { 3050bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // This will throw NumberFormatException on missing or malformed input. 3051bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden List<String> segments = uri.getPathSegments(); 3052bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long eventId = Long.parseLong(segments.get(1)); 3053bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long excepId = Long.parseLong(segments.get(2)); 3054bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that this is an exception instance (has an ORIGINAL_ID field 3055bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // that matches the supplied eventId) 3056bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return deleteEventInternal(excepId, callerIsSyncAdapter, false /* isBatch */); 3057bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 30589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 30599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3061b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 30627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 30631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, selection, 30641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 30657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 30689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 30707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3071b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 3072b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 30737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 30741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, null /* selection */, 30752fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 30767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 30799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, false, selection, selectionArgs, callerIsSyncAdapter); 30819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 30839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30841c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, true, null /*selection*/, null /*selectionArgs*/, 30851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden callerIsSyncAdapter); 30862fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30872fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 30882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 30892fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 3090b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 30912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 30921c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, selection, 3093b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 30942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30962fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 30972fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 30982fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 30992fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 3100b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 3101636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 31031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, 31041c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null /* selection */, null /* selectionArgs */); 31057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 31089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3110b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 31117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 31121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.CALENDAR_ALERTS, uri, selection, 31131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 31147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 31179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31182fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 31192fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 31209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3121b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 3122b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 31239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 31252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik StringBuilder selectionSb = new StringBuilder(Calendars._ID + "="); 31269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 31279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 31289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 31299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 31309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 31319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 313324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // $FALL-THROUGH$ - fall through to CALENDARS for the actual delete 31349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 31358d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 31368d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 313774ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 31389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 31399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 31406db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 3141315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 31429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 31439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 31449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 31459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 314810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 31491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 3150192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 31511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 31521ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 3153b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 3154b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 3155636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 31561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 31571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 31581ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 31591ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 31601ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 316148f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 31621ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 31631ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 31641ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 31651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 31661ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 31671ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 3168b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origId = cursor.getString(EVENTS_ORIGINAL_ID_INDEX); 3169b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origSyncId = cursor.getString(EVENTS_ORIGINAL_SYNC_ID_INDEX); 3170b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (isRecurrenceEvent(rrule, rdate, origId, origSyncId)) { 31711ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 31721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31734d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden boolean isRecurrence = !TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate); 31741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 317548f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 317648f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 3177bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // 3178bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The EVENTS_CLEANUP_TRIGGER_SQL trigger will remove all associated data 3179bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Attendees, Instances, Reminders, etc). 318048f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 3181b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 31824d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 31834d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // If this is a recurrence, and the event was never synced with the server, 31844d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we want to delete any exceptions as well. (If it has been to the server, 31854d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we'll let the sync adapter delete the events explicitly.) We assume that, 31864d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // if the recurrence hasn't been synced, the exceptions haven't either. 31874d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden if (isRecurrence && emptySyncId) { 31884d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID, selectionArgs); 31894d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden } 31901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 3191bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Event is on the server, so we "soft delete", i.e. mark as deleted so that 3192bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the sync adapter has a chance to tell the server about the deletion. After 3193bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the server sees the change, the sync adapter will do the "hard delete" 3194bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (above). 31951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 31961b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 3197c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 3198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 319902494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 32004d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // Exceptions that have been synced shouldn't be deleted -- the sync 32014d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // adapter will take care of that -- but we want to "soft delete" them so 32024d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // that they will be removed from the instances list. 32034d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // TODO: this seems to confuse the sync adapter, and leaves you with an 32044d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // invisible "ghost" event after the server sync. Maybe we can fix 32054d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // this by making instance generation smarter? Not vital, since the 32064d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exception instances disappear after the server sync. 32074d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden //mDb.update(Tables.EVENTS, values, SQL_WHERE_ORIGINAL_ID_HAS_SYNC_ID, 32084d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // selectionArgs); 32094d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 32104d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // It's possible for the original event to be on the server but have 32114d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exceptions that aren't. We want to remove all events with a matching 32124d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // original_id and an empty _sync_id. 32134d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID, 32144d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden selectionArgs); 32154d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 321643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 321743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 3218b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 3219b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 3220b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 3221b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 3222b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 3223b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 32241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 32251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 32261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 32271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 32281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 32291ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 32308f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 323110b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 3232420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3233dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 323410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 32351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 32361ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 32371ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 32387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 32391c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Delete rows from an Event-related table (e.g. Attendees) and mark corresponding events 32401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * as dirty. 32411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 32437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 32447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 32457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 32467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 32471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteFromEventRelatedTable(String table, Uri uri, String selection, 32481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden String[] selectionArgs) { 32491c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (table.equals(Tables.EVENTS)) { 32501c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("Don't delete Events with this method " 32511c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "(use deleteEventInternal)"); 32521c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 32551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 32561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Re-issue the delete URI as a query. Note that, if this is a by-ID request, the ID 32591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * will be in the URI, not selection/selectionArgs. 32601c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Note that the query will return data according to the access restrictions, 32621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * so we don't need to worry about deleting data we don't have permission to read. 32631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, GENERIC_EVENT_ID); 32657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 32667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 32671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long prevEventId = -1; 32681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 32691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long id = c.getLong(ID_INDEX); 32701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = c.getLong(EVENT_ID_INDEX); 32711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Duplicate the event. As a minor optimization, don't try to duplicate an 32721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // event that we just duplicated on the previous iteration. 32731c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 32741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 32751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden prevEventId = eventId; 32761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32779ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.delete(table, SQL_WHERE_ID, new String[]{String.valueOf(id)}); 32781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 32791c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 32801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId)} ); 32811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32827e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 32837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 32857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 32867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 32887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 32907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 32911c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Deletes rows from the Reminders table and marks the corresponding events as dirty. 32921c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Ensures the hasAlarm column in the Event is updated. 32931c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32941c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @return The number of rows deleted. 32951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32961c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteReminders(Uri uri, boolean byId, String selection, String[] selectionArgs, 32971c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden boolean callerIsSyncAdapter) { 32981c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32991c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this is a by-ID URI, make sure we have a good ID. Also, confirm that the 33001c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * selection is null, since we will be ignoring it. 33011c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33021c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long rowId = -1; 33031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 33041c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!TextUtils.isEmpty(selection)) { 33051c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 33061c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33071c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden rowId = ContentUris.parseId(uri); 33081c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (rowId < 0) { 33091c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 33101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33141c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Determine the set of events affected by this operation. There can be multiple 33151c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders with the same event_id, so to avoid beating up the database with "how many 33161c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders are left" and "duplicate this event" requests, we want to generate a list 33171c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * of affected event IDs and work off that. 33181c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 33191c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * TODO: use GROUP BY to reduce the number of rows returned in the cursor. (The content 33201c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * provider query() doesn't take it as an argument.) 33211c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden HashSet<Long> eventIdSet = new HashSet<Long>(); 33231c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, new String[] { Attendees.EVENT_ID }, selection, selectionArgs, null); 33241c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden try { 33251c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 33261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden eventIdSet.add(c.getLong(0)); 33271c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33281c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } finally { 33291c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden c.close(); 33301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this isn't a sync adapter, duplicate each event (along with its associated tables), 33341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * and mark each as "dirty". This is for the benefit of partial-update sync. 33351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33361c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!callerIsSyncAdapter) { 33371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 33381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 33391c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 33411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 33421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 33431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 33441c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 33451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 33461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33491c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33501c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Issue the original deletion request. If we were called with a by-ID URI, generate 33511c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * a selection. 33521c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 33541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selection = SQL_WHERE_ID; 33551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 33561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int delCount = mDb.delete(Tables.REMINDERS, selection, selectionArgs); 33581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33601c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * For each event, set "hasAlarm" to zero if we've deleted the last of the reminders. 33611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * (If the event still has reminders, hasAlarm should already be 1.) Because we're 33621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * executing in an exclusive transaction there's no risk of racing against other 33631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * database updates. 33641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33651c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues noAlarmValues = new ContentValues(); 33661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden noAlarmValues.put(Events.HAS_ALARM, 0); 33671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 33681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 33691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 33701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Count up the number of reminders still associated with this event. 33721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor reminders = mDb.query(Tables.REMINDERS, new String[] { GENERIC_ID }, 33731c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden SQL_WHERE_EVENT_ID, new String[] { String.valueOf(eventId) }, 33741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null, null, null); 33751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int reminderCount = reminders.getCount(); 33761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden reminders.close(); 33771c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (reminderCount == 0) { 33791c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, noAlarmValues, SQL_WHERE_ID, 33801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 33811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33831c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33841c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return delCount; 33851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33871c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 338824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Update rows in a table and, if this is a non-sync-adapter update, mark the corresponding 338924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * events as dirty. 339024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * <p> 339124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * This only works for tables that are associated with an event. It is assumed that the 339224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * link to the Event row is a numeric identifier in a column called "event_id". 339324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 339424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param uri The original request URI. 339524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param byId Set to true if the URI is expected to include an ID. 339624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param updateValues The new values to apply. Not all columns need be represented. 339724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selection For non-by-ID operations, the "where" clause to use. 339824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selectionArgs For non-by-ID operations, arguments to apply to the "where" clause. 339924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param callerIsSyncAdapter Set to true if the caller is a sync adapter. 340024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @return The number of rows updated. 34017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 340224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private int updateEventRelatedTable(Uri uri, String table, boolean byId, 340324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues updateValues, String selection, String[] selectionArgs, 340424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden boolean callerIsSyncAdapter) 340524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden { 340624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 340724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Confirm that the request has either an ID or a selection, but not both. It's not 340824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * actually "wrong" to have both, but it's not useful, and having neither is likely 340924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * a mistake. 341024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 341124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * If they provided an ID in the URI, convert it to an ID selection. 341224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 341324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (byId) { 341424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!TextUtils.isEmpty(selection)) { 341524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 341624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 341724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long rowId = ContentUris.parseId(uri); 341824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (rowId < 0) { 341924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 342024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 342124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selection = SQL_WHERE_ID; 342224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 342324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } else { 342424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (TextUtils.isEmpty(selection)) { 342524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection is required for " + uri); 342624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 342724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 342824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 342924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 343024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Query the events to update. We want all the columns from the table, so we us a 343124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * null projection. 343224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 343324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Cursor c = mDb.query(table, null /*projection*/, selection, selectionArgs, 343424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, null); 34357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 34367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 343724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (c.getCount() == 0) { 343824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.d(TAG, "No query results for " + uri + ", selection=" + selection + 343924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 344024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return 0; 344124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 344224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 344324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues dirtyValues = null; 344424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 344524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues = new ContentValues(); 344624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues.put(Events.DIRTY, "1"); 344724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 344824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 344924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int idIndex = c.getColumnIndex(GENERIC_ID); 345024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int eventIdIndex = c.getColumnIndex(GENERIC_EVENT_ID); 345124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (idIndex < 0 || eventIdIndex < 0) { 345224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new RuntimeException("Lookup on _id/event_id failed for " + uri); 345324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 345424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 345524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 345624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * For each row found: 345724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - merge original values with update values 345824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update database 345924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - if not sync adapter, set "dirty" flag in corresponding event to 1 346024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update Event attendee status 346124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 346224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden while (c.moveToNext()) { 346324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* copy the original values into a ContentValues, then merge the changes in */ 346424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues values = new ContentValues(); 346524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden DatabaseUtils.cursorRowToContentValues(c, values); 346624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden values.putAll(updateValues); 346724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 346824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long id = c.getLong(idIndex); 346924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = c.getLong(eventIdIndex); 347024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 347124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Make a copy of the original, so partial-update code can see diff. 347224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDbHelper.duplicateEvent(eventId); 347324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 347424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(table, values, SQL_WHERE_ID, new String[] { String.valueOf(id) }); 347524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 347624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 347724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden new String[] { String.valueOf(eventId) }); 347824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 34797e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 348024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 348124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 348224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * The Events table has a "selfAttendeeStatus" field that usually mirrors the 348324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * "attendeeStatus" column of one row in the Attendees table. It's the provider's 348424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * job to keep these in sync, so we have to check for changes here. (We have 348524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * to do it way down here because this is the only point where we have the 348624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * merged Attendees values.) 348724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 348824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * It's possible, but not expected, to have multiple Attendees entries with 348924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * matching attendeeEmail. The behavior in this case is not defined. 349024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 349124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * We could do this more efficiently for "bulk" updates by caching the Calendar 349224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * owner email and checking it here. 349324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 349424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (table.equals(Tables.ATTENDEES)) { 349524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden updateEventAttendeeStatus(mDb, values); 349624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 34977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 34997e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 35007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 35017e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 35027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 35037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 35042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int deleteMatchingColors(String selection, String[] selectionArgs) { 35052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // query to find all the colors that match, for each 35062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - verify no one references it 35072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - delete color 35082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, null, 35092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null, null); 35102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c == null) { 35112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return 0; 35122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 35142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c2 = null; 35152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 35162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String index = c.getString(COLORS_COLOR_INDEX_INDEX); 35172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = c.getString(COLORS_ACCOUNT_NAME_INDEX); 35182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = c.getString(COLORS_ACCOUNT_TYPE_INDEX); 35192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean isCalendarColor = c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 35202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 35212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (isCalendarColor) { 35222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = mDb.query(Tables.CALENDARS, ID_ONLY_PROJECTION, 35232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik SQL_WHERE_CALENDAR_COLOR, new String[] { 35242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName, accountType, index 35252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }, null, null, null); 35262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 35272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 35282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " calendars."); 35292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 35302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 35322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, SQL_WHERE_EVENT_COLOR, 35332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] {accountName, accountType, index}, null); 35342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 35352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 35362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " events."); 35372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 35382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 35412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2 != null) { 35422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2.close(); 35432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 35472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 35482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 35492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return mDb.delete(Tables.COLORS, selection, selectionArgs); 35522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 355474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 35559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 35569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 35579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 355874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 355974ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 356074ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 356174ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 356274ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 35639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 35649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 35659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 35679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 35689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 35699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 35709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 35729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 35739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 357474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 35759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3577fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 3578fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 3579fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3580fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 3581fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3582fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3583fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3584fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 3585fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 3586fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 3587fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3588fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3589fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 3590fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 3591fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3592fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 3593fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 3594fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 3595fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3596fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 3597fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 3598fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 3599d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean doesStatusCancelUpdateMeanUpdate(ContentValues values, 3600d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues modValues) { 3601d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isStatusCanceled = modValues.containsKey(Events.STATUS) && 3602d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden (modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 3603fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 3604d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String originalSyncId = values.getAsString(Events.ORIGINAL_SYNC_ID); 3605d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3606d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!TextUtils.isEmpty(originalSyncId)) { 3607d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This event is an exception. See if the recurring event still exists. 3608d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return doesEventExistForSyncId(originalSyncId); 3609d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3610d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3611d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This is the normal case, we just want an UPDATE 3612d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return true; 3613d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3614d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 36152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int handleUpdateColors(ContentValues values, String selection, String[] selectionArgs) { 36162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 36172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int result = mDb.update(Tables.COLORS, values, selection, selectionArgs); 36182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (values.containsKey(Colors.COLOR)) { 36192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 36202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, 36212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* groupBy */, null /* having */, null /* orderBy */); 36222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 36232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean calendarColor = 36242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 36252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = c.getInt(COLORS_COLOR_INDEX); 36262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String[] args = { 36272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_NAME_INDEX), 36282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_TYPE_INDEX), 36292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_COLOR_INDEX_INDEX) 36302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 36312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ContentValues colorValue = new ContentValues(); 36322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendarColor) { 36332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Calendars.CALENDAR_COLOR, color); 3634d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.CALENDARS, colorValue, SQL_WHERE_CALENDAR_COLOR, args); 36352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 36362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Events.EVENT_COLOR, color); 3637d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.EVENTS, colorValue, SQL_WHERE_EVENT_COLOR, args); 36382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 36412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 36422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 36432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return result; 36472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 3649d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3650d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 3651d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Handles a request to update one or more events. 3652d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 3653d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * The original event(s) will be loaded from the database, merged with the new values, 3654d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * and the result checked for validity. In some cases this will alter the supplied 3655d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * arguments (e.g. zeroing out the times on all-day events), change additional fields (e.g. 3656d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * update LAST_DATE when DTSTART changes), or cause modifications to other tables (e.g. reset 3657d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Instances when a recurrence rule changes). 3658d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3659d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param cursor The set of events to update. 36604b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden * @param updateValues The changes to apply to each event. 3661d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param callerIsSyncAdapter Indicates if the request comes from the sync adapter. 3662d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the number of rows updated 3663d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 36644b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden private int handleUpdateEvents(Cursor cursor, ContentValues updateValues, 3665d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean callerIsSyncAdapter) { 3666d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* 36671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * This field is considered read-only. It should not be modified by applications or 36681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * by the sync adapter. 36691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 36701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updateValues.remove(Events.HAS_ALARM); 36711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 36721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 3673d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For a single event, we can just load the event, merge modValues in, perform any 3674d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * fix-ups (putting changes into modValues), check validity, and then update(). We have 3675d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * to be careful that our fix-ups don't confuse the sync adapter. 3676d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3677d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For multiple events, we need to load, merge, and validate each event individually. 3678d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * If no single-event-specific changes need to be made, we could just issue the original 3679d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * bulk update, which would be more efficient than a series of individual updates. 3680d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * However, doing so would prevent us from taking advantage of the partial-update 3681d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * mechanism. 3682d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 3683d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (cursor.getCount() > 1) { 3684d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3685d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "Performing update on " + cursor.getCount() + " events"); 3686d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3687d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3688d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden while (cursor.moveToNext()) { 36899f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Make a copy of updateValues so we can make some local changes. 36904b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden ContentValues modValues = new ContentValues(updateValues); 36919f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 36929f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Load the event into a ContentValues object. 3693d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues values = new ContentValues(); 3694d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 36959f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden boolean doValidate = false; 36969f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (!callerIsSyncAdapter) { 36979f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden try { 36989f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Check to see if the data in the database is valid. If not, we will skip 36999f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // validation of the update, so that we don't blow up on attempts to 37009f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // modify existing badly-formed events. 37019f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden validateEventData(values); 37029f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden doValidate = true; 37039f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } catch (IllegalArgumentException iae) { 37049f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden Log.d(TAG, "Event " + values.getAsString(Events._ID) + 37059f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden " malformed, not validating update (" + 37069f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden iae.getMessage() + ")"); 37079f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 37089f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 37099f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 37109f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Merge the modifications in. 3711d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.putAll(modValues); 3712d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 37132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If a color_index is being set make sure it's valid 3714387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = modValues.getAsString(Events.EVENT_COLOR_KEY); 37152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 37162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 37172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 37182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.CALENDARS, ACCOUNT_PROJECTION, SQL_WHERE_ID, 37192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] { values.getAsString(Events.CALENDAR_ID) }, null, null, null); 37202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 37212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.moveToFirst()) { 37222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = c.getString(ACCOUNT_NAME_INDEX); 37232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = c.getString(ACCOUNT_TYPE_INDEX); 37242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 37262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 37272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 37282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_EVENT); 37312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 37339f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Scrub and/or validate the combined event. 3734be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 3735be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, modValues); 37369f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 37379f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (doValidate) { 3738be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 3739be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 3740d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3741d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Look for any updates that could affect LAST_DATE. It's defined as the end of 3742d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // the last meeting, so we need to pay attention to DURATION. 3743d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3744d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DTEND) || 3745d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DURATION) || 3746d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EVENT_TIMEZONE) || 3747d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RRULE) || 3748d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RDATE) || 3749d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXRULE) || 3750d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXDATE)) { 3751d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long newLastDate; 3752d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3753d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden newLastDate = calculateLastDate(values); 3754d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } catch (DateException de) { 3755d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Unable to compute LAST_DATE", de); 3756d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3757d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long oldLastDateObj = values.getAsLong(Events.LAST_DATE); 3758d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long oldLastDate = (oldLastDateObj == null) ? -1 : oldLastDateObj; 3759d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (oldLastDate != newLastDate) { 3760d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This overwrites any caller-supplied LAST_DATE. This is okay, because the 3761d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // caller isn't supposed to be messing with the LAST_DATE field. 3762d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (newLastDate < 0) { 3763d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.putNull(Events.LAST_DATE); 3764d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3765d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.LAST_DATE, newLastDate); 3766fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3767fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3768d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3769d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3770d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3771d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DIRTY, 1); 3772d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3773fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3774d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Disallow updating the attendee status in the Events 3775d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // table. In the future, we could support this but we 3776d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // would have to query and update the attendees table 3777d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to keep the values consistent. 3778d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 3779d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Updating " 3780d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + Events.SELF_ATTENDEE_STATUS 3781d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + " in Events table is not allowed."); 3782d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3783d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3784d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(values, modValues)) { 3785d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.WARN)) { 3786d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.w(TAG, "handleUpdateEvents: " + 3787d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "allDay is true but sec, min, hour were not 0."); 3788fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3789d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3790d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3791d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // For taking care about recurrences exceptions cancelations, check if this needs 3792d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to be an UPDATE or a DELETE 3793d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isUpdate = doesStatusCancelUpdateMeanUpdate(values, modValues); 3794d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3795d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = values.getAsLong(Events._ID); 3796d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3797d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (isUpdate) { 3798d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If a user made a change, possibly duplicate the event so we can do a partial 3799d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // update. If a sync adapter made a change and that change marks an event as 3800d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // un-dirty, remove any duplicates that may have been created earlier. 3801d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3802d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.duplicateEvent(id); 3803d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3804d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DIRTY) 3805d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden && modValues.getAsInteger(Events.DIRTY) == 0) { 3806d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.removeDuplicateEvent(id); 3807d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3808d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3809d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int result = mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 3810d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden new String[] { String.valueOf(id) }); 3811d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (result > 0) { 3812d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden updateEventRawTimesLocked(id, modValues); 3813d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mInstancesHelper.updateInstancesLocked(modValues, id, 3814d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden false /* not a new event */, mDb); 3815d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3816d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // XXX: should we also be doing this when RRULE changes (e.g. instances 3817d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // are introduced or removed?) 3818d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3819d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.STATUS)) { 3820d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If this is a cancellation knock it out 3821d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // of the instances table 3822d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.STATUS) && 3823d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 3824d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String[] args = new String[] {String.valueOf(id)}; 3825d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 3826d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3827d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3828d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // The start time or status of the event changed, so run the 3829d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // event alarm scheduler. 3830d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3831d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "updateInternal() changing event"); 3832d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3833d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3834d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3835d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3836d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(id, callerIsSyncAdapter); 3837d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3838d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3839d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3840d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3841d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(callerIsSyncAdapter); 3842fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3843fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3844d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3845d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return cursor.getCount(); 3846fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3847fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 38489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 38499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 3850b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 3851ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 38529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 38539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38548d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 38550739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 38560739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_UPDATE, uri, values, callerIsSyncAdapter, match, 38570739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 38589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 38609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 38619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 38628d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 38638d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE), selectionArgs); 38649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 38668d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 38678d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 38682ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 3869dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 38709323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3871dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3872dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3873dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 38749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38762f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 38772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Integer color = values.getAsInteger(Colors.COLOR); 38782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (values.size() > 1 || (values.size() == 1 && color == null)) { 38792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("You may only change the COLOR " 38802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + "for an existing Colors entry."); 38812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38828d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return handleUpdateColors(values, appendAccountToSelection(uri, selection, 38838d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE), 38842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 38852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 388643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 38879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 38889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 388943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 389043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 389143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 389243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 389343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 389443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 389543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 389643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 389743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 38984cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik if (selection != null && TextUtils.equals(selection,"_id=?")) { 38994cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik id = Long.parseLong(selectionArgs[0]); 39004cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik } else if (selection != null && selection.startsWith("_id=")) { 390143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 390243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 390343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 390443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 3905b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 390643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 390743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 390843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 3909c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Calendars.DIRTY, 1); 39102fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 39119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 39129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 39139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 39149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3915387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 39162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 39172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 39182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 39192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 39202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(id); 39212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 39222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 39232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 39242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 39252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 39262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_CALENDAR); 39272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 39289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3929b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 3930636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 39319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39323ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3933d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 39344067700dbedcf4c8a379c9ecba9b5603972b4607Andy McFadden if (values.containsKey(Calendars.VISIBLE)) { 3935d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3936d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3937d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3938d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3939420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 3940d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 39413ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3942dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 39433ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 39443ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 39459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 39469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 39489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 39499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 3950d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Cursor events = null; 39519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3952d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Grab the full set of columns for each selected event. 3953d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: define a projection with just the data we need (e.g. we don't need to 3954d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // validate the SYNC_* columns) 39559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3956d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3957d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (match == EVENTS_ID) { 3958d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Single event, identified by ID. 3959d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = ContentUris.parseId(uri); 3960d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3961d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden SQL_WHERE_ID, new String[] { String.valueOf(id) }, 3962d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 39639ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } else { 3964d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // One or more events, identified by the selection / selectionArgs. 3965d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3966d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden selection, selectionArgs, 3967d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 39689ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 396906c305d35741db303bd3aacd0eab5af8de0ab34eErik 3970d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events.getCount() == 0) { 397124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.i(TAG, "No events to update: uri=" + uri + " selection=" + selection + 3972d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 3973d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return 0; 3974d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 39753ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3976d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return handleUpdateEvents(events, values, callerIsSyncAdapter); 3977d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } finally { 3978d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events != null) { 3979d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events.close(); 3980fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 39819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 398324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES: 398424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, false, values, selection, 398524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 398624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES_ID: 398724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, true, values, null, null, 398824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 39899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39902fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 39912fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 39922fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 39939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3994b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 3995636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 39969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39972fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 39982fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 39992fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 4000b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 40019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 400224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 400324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case REMINDERS: 400424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.REMINDERS, false, values, selection, 400524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 40062fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 400724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int count = updateEventRelatedTable(uri, Tables.REMINDERS, true, values, null, null, 400824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 40097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 40109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 40119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 40129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 40139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 40149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4015420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 40167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 40179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 401824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 401924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case EXTENDED_PROPERTIES_ID: 402024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.EXTENDED_PROPERTIES, true, values, 402124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, callerIsSyncAdapter); 402224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 402383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 402483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 402583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 4026420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 402783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 402883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 402983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 4030420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(true); 403183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 403283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 40339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4034315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 4035315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 4036315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 4037315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4038315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4039315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 4040315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4041315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 4042315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 4043315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 4044315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4045315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4046315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 4047315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 4048315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4049315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 4050315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 4051b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 4052315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4053315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 4054f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "home", set the Instances 4055f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the previous 4056f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "auto", set the Instances 4057f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the current 4058f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // device one 4059f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone Instances is set AND if we are in "home" 4060f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone type, then save the timezone Instance into 4061f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // "previous" too 4062315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 4063315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 4064315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 4065315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 4066315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 4067315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 4068315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 4069315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 4070315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 4071315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 4072315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 4073315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4074315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 4075d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 4076315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 4077315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4078d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4079315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4080315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4081315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 4082315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 4083315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 4084315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 4085315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 4086315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4087d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4088315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4089315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4090315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4091315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4092315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 4093315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 4094315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 4095315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 4096315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 4097315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 4098315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 4099315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 4100d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 4101315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 4102315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 4103315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4104d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4105315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4106315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4107315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4108315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4109315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 4110315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4111315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 41129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 41139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 41149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik /** 41182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * Verifies that a color with the given index exists for the given Calendar 41192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * entry. 41202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * 41212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountName The email of the account the color is for 41222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountType The type of account the color is for 41234755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorIndex The color_index being set for the calendar 41244755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorType The type of color expected (Calendar/Event) 41252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @return The color specified by the index 41262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik */ 41274755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private int verifyColorExists(String accountName, String accountType, String colorIndex, 41284755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan int colorType) { 41292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 41302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Cannot set color. A valid account does" 41312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " not exist for this calendar."); 41322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color; 41342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 41352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 41364755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 41374755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan if (!c.moveToFirst()) { 41384755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("Color type: " + colorType + " and index " 41394755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + colorIndex + " does not exist for account."); 41402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik color = c.getInt(COLORS_COLOR_INDEX); 41422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 41432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 41442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 41452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return color; 41482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 41509ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendLastSyncedColumnToSelection(String selection, Uri uri) { 41519ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (getIsCallerSyncAdapter(uri)) { 41529ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 4153595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 41549ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 4155b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sb.append(CalendarContract.Events.LAST_SYNCED).append(" = 0"); 41569ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 4157595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 4158595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 41598d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private String appendAccountToSelection( 41608d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Uri uri, 41618d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String selection, 41628d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String accountNameColumn, 41638d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String accountTypeColumn) { 41640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountName = QueryParameterUtils.getQueryParameter(uri, 4165b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 41660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountType = QueryParameterUtils.getQueryParameter(uri, 4167b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 41680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (!TextUtils.isEmpty(accountName)) { 41698d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert final StringBuilder sb = new StringBuilder() 41708d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(accountNameColumn) 41718d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append("=") 41728d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(DatabaseUtils.sqlEscapeString(accountName)) 41738d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(" AND ") 41748d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(accountTypeColumn) 41758d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append("=") 41768d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(DatabaseUtils.sqlEscapeString(accountType)); 41778d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return appendSelection(sb, selection); 41789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 41799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 41809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41839ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendSelection(StringBuilder sb, String selection) { 41849ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!TextUtils.isEmpty(selection)) { 41859ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(" AND ("); 41869ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(selection); 41879ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(')'); 41889ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41899ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return sb.toString(); 41909ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41919ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 41920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik /** 41930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * Verifies that the operation is allowed and throws an exception if it 41940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * isn't. This defines the limits of a sync adapter call vs an app call. 4195683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * <p> 4196683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * Also rejects calls that have a selection but shouldn't, or that don't have a selection 4197683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * but should. 4198c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik * 41990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param type The type of call, {@link #TRANSACTION_QUERY}, 42000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_INSERT}, {@link #TRANSACTION_UPDATE}, or 42010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_DELETE} 42020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param uri 42030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param values 42040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param isSyncAdapter 42050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik */ 42060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyTransactionAllowed(int type, Uri uri, ContentValues values, 42070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik boolean isSyncAdapter, int uriMatch, String selection, String[] selectionArgs) { 4208f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Queries are never restricted to app- or sync-adapter-only, and we don't 4209f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // restrict the set of columns that may be accessed. 4210f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden if (type == TRANSACTION_QUERY) { 4211f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden return; 4212f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4213f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 4214683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (type == TRANSACTION_UPDATE || type == TRANSACTION_DELETE) { 42152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // TODO review this list, document in contract. 4216683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (!TextUtils.isEmpty(selection)) { 4217683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Only allow selections for the URIs that can reasonably use them. 42182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Whitelist of URIs allowed selections 4219683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4220683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case SYNCSTATE: 4221683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDARS: 4222683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4223683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4224683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDAR_ALERTS: 4225683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4226683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EXTENDED_PROPERTIES: 4227683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 42282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 4229683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4230683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4231683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection not permitted for " + uri); 4232683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4233683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } else { 4234683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Disallow empty selections for some URIs. 42352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Blacklist of URIs _not_ allowed empty selections 4236683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4237683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4238683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4239683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4240683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 4241683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection must be specified for " 4242683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden + uri); 4243683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4244683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4245683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4246683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4247683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4248683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden 4249f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Only the sync adapter can use these to make changes. 42502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!isSyncAdapter) { 42512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik switch (uriMatch) { 42522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE: 42532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE_ID: 42542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES: 42552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES_ID: 42562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 42572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Only sync adapters may write using " + uri); 42582f251c778c06d21ed7693a70f4a1268ff929242eRoboErik default: 42592f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 4260f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4261f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4262f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 42630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (type) { 42640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_INSERT: 42650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException( 42670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Inserting into instances not supported"); 42680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4269c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4270c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 42710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 42740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 42750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 42760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 42770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 42790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_UPDATE: 42800739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Updating instances not supported"); 42820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4283c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4284c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 42850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 42880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 42890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 42900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 42910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 42930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_DELETE: 42940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Deleting instances not supported"); 42960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 43000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 43050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyHasAccount(Uri uri, String selection, String[] selectionArgs) { 4306c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = QueryParameterUtils.getQueryParameter(uri, Calendars.ACCOUNT_NAME); 43070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String accountType = QueryParameterUtils.getQueryParameter(uri, 4308c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 43090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 43100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (selection != null && selection.startsWith(ACCOUNT_SELECTION_PREFIX)) { 43110739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountName = selectionArgs[0]; 43120739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountType = selectionArgs[1]; 43130739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43140739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43150739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 43160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException( 43170739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Sync adapters must specify an account and account type: " + uri); 43180739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43190739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43200739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 4321c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private void verifyColumns(ContentValues values, int uriMatch) { 4322c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 4323c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik return; 4324c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4325c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String[] columns; 4326c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik switch (uriMatch) { 4327c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4328c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4329c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4330c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4331c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = Events.PROVIDER_WRITABLE_COLUMNS; 4332c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4333c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik default: 4334c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = PROVIDER_WRITABLE_DEFAULT_COLUMNS; 4335c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4336c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4337c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 4338c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik for (int i = 0; i < columns.length; i++) { 4339c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values.containsKey(columns[i])) { 4340c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik throw new IllegalArgumentException("Only the provider may write to " + columns[i]); 4341c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4342c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4343c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4344c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 43450739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyNoSyncColumns(ContentValues values, int uriMatch) { 4346c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 43470739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43480739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String[] syncColumns; 43500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (uriMatch) { 43510739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS: 43520739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS_ID: 43530739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES: 43540739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES_ID: 4355c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Calendars.SYNC_WRITABLE_COLUMNS; 4356c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4357c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4358c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4359c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4360c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4361c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Events.SYNC_WRITABLE_COLUMNS; 43620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 43630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik default: 43640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik syncColumns = SYNC_WRITABLE_DEFAULT_COLUMNS; 43650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 43660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 43670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik for (int i = 0; i < syncColumns.length; i++) { 43690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (values.containsKey(syncColumns[i])) { 43700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException("Only sync adapters may write to " 43710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + syncColumns[i]); 43720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 43769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 43779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 43789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 43799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 4380c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik new String[] {Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, 4381fa332ecedc0c340109811552407142f6e4f600b2RoboErik Calendars.CAL_SYNC1, Calendars.SYNC_EVENTS}, 43829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 43839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 43849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 43859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 43869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 43879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 43889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 4389ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 43909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 4391ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 4392ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 4393ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 4394ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 4395ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 4396ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 4397ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 43989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 43992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) 44002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 44019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 44049535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 44059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 4406f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 4407f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 4408f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 4409f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 44109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 44119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 44139535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 44149535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 44159535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 44169535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 44179535627bf6295cd94447beb83e1aac41f50c3600Erik } 44189535627bf6295cd94447beb83e1aac41f50c3600Erik 44199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 44209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 44219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 44229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 44249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 44259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 44269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 44279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4429a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4430a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4431a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4432a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 4433dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 44349ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4435a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4436dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 4437dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 4438dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 4439a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4440a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4441a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4442a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4443a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4444a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 4445a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 4446a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 4447a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 4448a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 4449a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 44509ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param eventId the ID of the event that changed, or -1 for no specific event 44519ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4452a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4453dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 4454dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 4455a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 4456a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 4457a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 4458a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 4459a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 4460dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 4461dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 4462dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 4463dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 4464dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 4465dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 4466dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 4467dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 4468dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 4469dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 4470dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 4471dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 4472dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 4473dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 4474dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 4475dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 4476dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 4477a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 4478dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 4479a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4480a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4481a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4482a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 4483a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 44849ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #sendUpdateNotification(boolean)} instead. 4485a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 44869ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @see #sendUpdateNotification(boolean) 4487a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4488a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 4489a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 4490b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.CONTENT_URI); 4491f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 4492f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 4493f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 4494e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 4495a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4496a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 44970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_QUERY = 0; 44980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_INSERT = 1; 44990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_UPDATE = 2; 45000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_DELETE = 3; 45010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 45020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:off 45030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final String[] SYNC_WRITABLE_DEFAULT_COLUMNS = new String[] { 4504b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars.DIRTY, 4505b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars._SYNC_ID 45060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik }; 4507c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String[] PROVIDER_WRITABLE_DEFAULT_COLUMNS = new String[] { 4508c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik }; 45090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:on 45100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 45119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 45129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 45139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 45142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS = 4; 45152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS_ID = 5; 45162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES = 6; 45172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES_ID = 7; 45182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS = 8; 45192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS_ID = 9; 45202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES = 10; 45212ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES_ID = 11; 45222ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS = 12; 45232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_ID = 13; 45242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_BY_INSTANCE = 14; 45252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_BY_DAY = 15; 45262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE = 16; 45272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE_ID = 17; 45282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES = 18; 45292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES_ID = 19; 45302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_DAYS = 20; 45312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM = 21; 45322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM_REMOVE = 22; 45332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int TIME = 23; 45342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES = 24; 45352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES_ID = 25; 45362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH = 26; 45372ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH_BY_DAY = 27; 45382ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int PROVIDER_PROPERTIES = 28; 4539bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID = 29; 4540bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID2 = 30; 45413b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static final int EMMA = 31; 45422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS = 32; 45439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 45459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 45462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final HashMap<String, String> sColorsProjectionMap; 4547f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final HashMap<String, String> sEventsProjectionMap; 454819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 45499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 45509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 45519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 4552315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 455339c65e5716e21e863d8de587d139dae85f99422fFred Quintana private static final HashMap<String, String> sCountProjectionMap; 45549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 4556b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/when/*/*", INSTANCES); 4557b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 4558b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 4559b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/searchbyday/*/*/*", 456081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 4561b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 4562b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events", EVENTS); 4563b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events/#", EVENTS_ID); 4564b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities", EVENT_ENTITIES); 4565b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 4566b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars", CALENDARS); 4567b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars/#", CALENDARS_ID); 4568b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 4569b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 4570b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees", ATTENDEES); 4571b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees/#", ATTENDEES_ID); 4572b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders", REMINDERS); 4573b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders/#", REMINDERS_ID); 4574b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 4575b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties/#", 4576b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik EXTENDED_PROPERTIES_ID); 4577b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 4578b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 4579b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/by_instance", 4580b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 4581b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate", SYNCSTATE); 4582b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 4583b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, CalendarAlarmManager.SCHEDULE_ALARM_PATH, 4584420b7fb569773ae573fbe90c3a9c522d4c368863Erik SCHEDULE_ALARM); 4585b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, 4586b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 4587b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time/#", TIME); 4588b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time", TIME); 4589b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "properties", PROVIDER_PROPERTIES); 4590b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID); 4591b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2); 45923b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden sUriMatcher.addURI(CalendarContract.AUTHORITY, "emma", EMMA); 45932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "colors", COLORS); 45949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 459539c65e5716e21e863d8de587d139dae85f99422fFred Quintana /** Contains just BaseColumns._COUNT */ 459639c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap = new HashMap<String, String>(); 459739c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)"); 459839c65e5716e21e863d8de587d139dae85f99422fFred Quintana 45992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap = new HashMap<String, String>(); 46002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors._ID, Colors._ID); 46012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.DATA, Colors.DATA); 46022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_NAME, Colors.ACCOUNT_NAME); 46032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_TYPE, Colors.ACCOUNT_TYPE); 4604387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sColorsProjectionMap.put(Colors.COLOR_KEY, Colors.COLOR_KEY); 46052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR_TYPE, Colors.COLOR_TYPE); 46062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR, Colors.COLOR); 46072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 46089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 46099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 461002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME); 461102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_TYPE, Events.ACCOUNT_TYPE); 4612c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.TITLE, Events.TITLE); 4613c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4614c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4615c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.STATUS, Events.STATUS); 461602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4617387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR_KEY, Events.EVENT_COLOR_KEY); 4618c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4619c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART); 4620c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTEND, Events.DTEND); 4621c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4622c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4623c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DURATION, Events.DURATION); 4624c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4625c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4626c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4627c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4628c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, Events.HAS_EXTENDED_PROPERTIES); 4629c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RRULE, Events.RRULE); 4630c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RDATE, Events.RDATE); 4631c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXRULE, Events.EXRULE); 4632c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXDATE, Events.EXDATE); 4633c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 463434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4635c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, Events.ORIGINAL_INSTANCE_TIME); 4636c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4637c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4638c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4639c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4640c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, Events.GUESTS_CAN_INVITE_OTHERS); 4641c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4642c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4643c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4644c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventsProjectionMap.put(Events.CUSTOM_APP_PACKAGE, Events.CUSTOM_APP_PACKAGE); 4645c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventsProjectionMap.put(Events.CUSTOM_APP_URI, Events.CUSTOM_APP_URI); 4646c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DELETED, Events.DELETED); 464702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 46489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4649e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 46501ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 46511ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 46521ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 46539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 4654c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 4655387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR_KEY); 465602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL); 4657c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 465802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 4659c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 466002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 466102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 46622f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap 46632f251c778c06d21ed7693a70f4a1268ff929242eRoboErik .put(Calendars.ALLOWED_ATTENDEE_TYPES, Calendars.ALLOWED_ATTENDEE_TYPES); 46642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap.put(Calendars.ALLOWED_AVAILABILITY, Calendars.ALLOWED_AVAILABILITY); 466502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 466602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND); 466702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 4668c339afc7df041ebfc5f4587f78cf38562aa23459Alon Albert sEventsProjectionMap.put(Events.DISPLAY_COLOR, Events.DISPLAY_COLOR); 46699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4670982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 4671e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 4672e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 4673982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4674e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4675982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 4676c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events._ID, Events._ID); 467702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 467802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 467902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 468002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 468102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 468202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 46839ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 468402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 468502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 468602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 468702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 468802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 468902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 469002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 469102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 469202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 469302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 469402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 469502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 469602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 4697c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY); 46989ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 46999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 470046f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 4701c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.TITLE, Events.TITLE); 4702c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4703c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4704c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.STATUS, Events.STATUS); 470502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4706c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4707c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTSTART, Events.DTSTART); 4708c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTEND, Events.DTEND); 4709c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4710c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4711c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DURATION, Events.DURATION); 4712c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4713c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4714c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4715c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4716c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, 4717c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.HAS_EXTENDED_PROPERTIES); 4718c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RRULE, Events.RRULE); 4719c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RDATE, Events.RDATE); 4720c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXRULE, Events.EXRULE); 4721c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXDATE, Events.EXDATE); 4722c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 472334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4724c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, 4725c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_INSTANCE_TIME); 4726c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4727c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4728c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4729c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4730c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, 4731c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.GUESTS_CAN_INVITE_OTHERS); 4732c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4733c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4734c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4735c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventEntitiesProjectionMap.put(Events.CUSTOM_APP_PACKAGE, Events.CUSTOM_APP_PACKAGE); 4736c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventEntitiesProjectionMap.put(Events.CUSTOM_APP_URI, Events.CUSTOM_APP_URI); 4737c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DELETED, Events.DELETED); 473819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 473919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 474002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 474102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 474202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 474302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 474402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 474502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 47469ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 474702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 474802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 474902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 4750470aa5bc291ca33d51dda356f38ac2954026da9aAlon Albert sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY); 47519ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 4752fa332ecedc0c340109811552407142f6e4f600b2RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 475302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 475402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 475502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 475602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 475702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 475802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 475902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 476002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 476102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 476219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 47639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 47641b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 47659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 47669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 47679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 47689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 47699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 47709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 47719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 47729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 47739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 47759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 47769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 47779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 47789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 47799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 47809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 47819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 4782bafe9de156292f65b1079dd1eb586669f573d9e6Michael Chan sAttendeesProjectionMap.put(Attendees.ATTENDEE_IDENTITY, "attendeeIdentity"); 4783bafe9de156292f65b1079dd1eb586669f573d9e6Michael Chan sAttendeesProjectionMap.put(Attendees.ATTENDEE_ID_NAMESPACE, "attendeeIdNamespace"); 478402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 478502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 47869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 47889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 47899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 47909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 47919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 4792361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 4793361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 47949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 47969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 47979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 47989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 47999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 48009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 48019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 48029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4803315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4804315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4805315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4806315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4807315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 48089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 481064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 48119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 481264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * This is called by AccountManager when the set of accounts is updated. 481364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * <p> 481464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * We are overriding this since we need to delete from the 48159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 48167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 48177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 481864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * 481964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * @param accounts The list of currently active accounts. 48209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4821f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik @Override 48229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 482364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Thread thread = new AccountsUpdatedThread(accounts); 482464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden thread.start(); 482564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 482664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 482764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private class AccountsUpdatedThread extends Thread { 482864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private Account[] mAccounts; 482964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 483064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountsUpdatedThread(Account[] accounts) { 483164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden mAccounts = accounts; 483264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 483364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 483464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden @Override 483564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden public void run() { 483664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // The process could be killed while the thread runs. Right now that isn't a problem, 483764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // because we'll just call removeStaleAccounts() again when the provider restarts, but 483864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // if we want to do additional actions we may need to use a service (e.g. start 483964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // EmptyService in onAccountsUpdated() and stop it when we finish here). 484064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 484164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 484264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(mAccounts); 484364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 484464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 484564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 484664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden /** 484764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * Makes sure there are no entries for accounts that no longer exist. 484864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden */ 484964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void removeStaleAccounts(Account[] accounts) { 4850ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4851ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4852ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4853ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4854ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4855ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 48569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 485746f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 48589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 48599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 48609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 48629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 48642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 48659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 48669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik for (String table : new String[]{Tables.CALENDARS, Tables.COLORS}) { 4868ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 48699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 48702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.rawQuery("SELECT DISTINCT " + 48712ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_NAME + 48727cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 48732ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + 48747cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 48759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 48764cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // ACCOUNT_TYPE_LOCAL is to store calendars not associated 48774cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // with a system account. Typically, a calendar must be 48784cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // associated with an account on the device or it will be 48794cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // deleted. 4880b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (c.getString(0) != null 4881b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && c.getString(1) != null 4882b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && !TextUtils.equals(c.getString(1), 4883b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.ACCOUNT_TYPE_LOCAL)) { 48849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 48859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 48869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 48879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 48912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = null; 48929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 4895f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 4896f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 4897f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 48989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 4899b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 49002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // This will be a no-op for accounts without a color palette. 49012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik mDb.execSQL(SQL_DELETE_FROM_COLORS, params); 49029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 49039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 49049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 49059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 49062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 49072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 49082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 49099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 49109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 49113ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 49123ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 4913dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 49149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 49159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4916636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 4917636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 4918636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 4919636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 4920636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 4921636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 4922636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 4923636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 4924636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 4925636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 4926636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 4927636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 4928636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 4929636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 4930636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 4931636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 4932636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 4933636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 4934636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 49359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 4936