CalendarProvider2.java revision 8253a84ce7abf2fa1c662b735432a502f4ace96f
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 20d5af586101b6111ca188bb373098309c7c8a4abbAlon Albertimport com.google.common.annotations.VisibleForTesting; 21d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert 22bf61571797b7b6a390d35f16aad7765ea348e5aeAndy McFaddenimport com.android.calendarcommon.DateException; 2393e0bbb921cce7a5cec355521bc570c03c9d6a1cAndy McFaddenimport com.android.calendarcommon.EventRecurrence; 24bf61571797b7b6a390d35f16aad7765ea348e5aeAndy McFaddenimport com.android.calendarcommon.RecurrenceProcessor; 2593e0bbb921cce7a5cec355521bc570c03c9d6a1cAndy McFaddenimport com.android.calendarcommon.RecurrenceSet; 267be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 277be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 28370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 46a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 47a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 50b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract; 51b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Attendees; 52b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.CalendarAlerts; 53b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Calendars; 542f251c778c06d21ed7693a70f4a1268ff929242eRoboErikimport android.provider.CalendarContract.Colors; 55b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Events; 56b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Instances; 57b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Reminders; 58b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.SyncState; 599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 601edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 61192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 64ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 663b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.io.File; 672ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErikimport java.lang.reflect.Array; 683b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.lang.reflect.Method; 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 70ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 731c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFaddenimport java.util.Iterator; 74dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 75bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFaddenimport java.util.Set; 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 77dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 82b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik * is defined in {@link android.provider.CalendarContract}. 839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 878bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 88d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden static final boolean DEBUG_INSTANCES = false; 899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 907be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 91c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String ACCOUNT_SELECTION_PREFIX = Calendars.ACCOUNT_NAME + "=? AND " 92c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik + Calendars.ACCOUNT_TYPE + "=?"; 937be45683e367bd6897daf6444b03be938f8f5eaaErik 94f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final boolean PROFILE = false; 959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 968f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 1019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 1029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 104b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Events.ORIGINAL_ID, 105c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_SYNC_ID, 1069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1079ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 1089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 111b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_ID_INDEX = 3; 112b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4; 1137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] COLORS_PROJECTION = new String[] { 1152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_NAME, 1162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_TYPE, 1172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR_TYPE, 118387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik Colors.COLOR_KEY, 1192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR, 1202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_NAME_INDEX = 0; 1222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_TYPE_INDEX = 1; 1232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_TYPE_INDEX = 2; 1242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX_INDEX = 3; 1252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX = 4; 1262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1274755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private static final String COLOR_FULL_SELECTION = Colors.ACCOUNT_NAME + "=? AND " 1284755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + Colors.ACCOUNT_TYPE + "=? AND " + Colors.COLOR_TYPE + "=? AND " + Colors.COLOR_KEY 1294755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + "=?"; 1304755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan 1312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_NAME = Calendars.ACCOUNT_NAME; 1322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_TYPE = Calendars.ACCOUNT_TYPE; 1332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] ACCOUNT_PROJECTION = new String[] { 1342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_NAME, 1352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_TYPE, 1362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_NAME_INDEX = 0; 1382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_TYPE_INDEX = 1; 1392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // many tables have _id and event_id; pick a representative version to use as our generic 1411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_ID = Attendees._ID; 1421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_EVENT_ID = Attendees.EVENT_ID; 1431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 1447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_ID, 1461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_EVENT_ID, 1477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 152646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 153646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 154646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 155646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 156646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 157646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 158646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 159646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 160646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 161646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 162646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 163646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 164646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 165646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 166646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 167646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 173ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 176f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik private CalendarInstancesHelper mInstancesHelper; 1779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1788ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 179f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // Due to an issue in Calendar Server restricting the length of the name we 180f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // had to strip it down 1818ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1828ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1838ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1848ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1858ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1863443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 187b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + ", " + 188b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTSTART_2445 + ", " + 189b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTEND_2445 + ", " + 1903443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1913443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 192b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 193b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1943443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 195b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 196b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 197b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " + 198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 199c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik " SET " + Events.DIRTY + "=1" + 200b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 201b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_WHERE_CALENDAR_COLOR = Calendars.ACCOUNT_NAME + "=? AND " 203387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik + Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.CALENDAR_COLOR_KEY + "=?"; 2042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 2052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_WHERE_EVENT_COLOR = Events.ACCOUNT_NAME + "=? AND " 206387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik + Events.ACCOUNT_TYPE + "=? AND " + Events.EVENT_COLOR_KEY + "=?"; 2072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 20824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden protected static final String SQL_WHERE_ID = GENERIC_ID + "=?"; 20924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private static final String SQL_WHERE_EVENT_ID = GENERIC_EVENT_ID + "=?"; 2104d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?"; 2114d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID = Events.ORIGINAL_ID + 2124d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden "=? AND " + Events._SYNC_ID + " IS NULL"; 213ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 214ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan private static final String SQL_WHERE_ATTENDEE_BASE = 215ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID 216ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan + " AND " + 217ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 218ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 219b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 220ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + SQL_WHERE_ATTENDEE_BASE; 221b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 222b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 223b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 224ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + 225ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan " AND " + 226ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 227b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 228b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 2292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 230b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 231b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 232b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 2332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 234b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 235b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 236b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 237b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 238b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 239b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik Tables.EXTENDED_PROPERTIES + "." + CalendarContract.ExtendedProperties._ID + "=?"; 240b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 241b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 2422ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik " WHERE " + Calendars.ACCOUNT_NAME + "=? AND " + 2432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + "=?"; 244b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_DELETE_FROM_COLORS = "DELETE FROM " + Tables.COLORS + " WHERE " 2462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?"; 2472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 248fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 249fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 250fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 26081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 26181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 26281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 26381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 264b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 26581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 266b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 26781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 26818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 26918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 27018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 27118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 27218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 273b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 27418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 275b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")" + ") LEFT OUTER JOIN " + 27618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 27718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 278b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Attendees.EVENT_ID + "=" + 27918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 280b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 28118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 282b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 283b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.START_DAY + "<=? AND " + 284b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END_DAY + ">=?"; 28581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 286b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 287b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.BEGIN + "<=? AND " + 288b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END + ">=?"; 2899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 2972ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * The sort order is: events with an earlier start time occur first and if 2982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the start times are the same, then events with a later end time occur 2992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * first. The later end time is ordered first so that long-running events in 3002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the calendar views appear first. If the start and end times of two events 3012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * are the same then we sort alphabetically on the title. This isn't 3022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * required for correctness, it just adds a nice touch. 3032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 3042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; 3052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 3062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 3072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * A regex for describing how we split search queries into tokens. Keeps 3082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * quoted phrases as one token. "one \"two three\"" ==> ["one" "two three"] 309dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 310dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 311dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 312dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 313dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 314dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 315dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 316dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 317dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 318dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 31981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 320dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 321dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 322dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 323dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 324dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 325dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 326dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 327dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 328dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 32981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 33018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 33118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 33218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 33318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 33418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 335b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_EMAIL + ")"; 33618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 33718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 33818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 33918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 34018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 34118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 342b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_NAME + ")"; 34318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 34481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 345b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.TITLE, 346b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.DESCRIPTION, 347b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.EVENT_LOCATION, 34818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 34918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 35081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 35181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 352a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 353a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 354a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 355a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 356a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 357a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 358a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 359a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 360a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 361a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 362a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 363a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 364dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 365dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 366dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 367dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 368dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 369bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Set of columns allowed to be altered when creating an exception to a recurring event. */ 370bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final HashSet<String> ALLOWED_IN_EXCEPTION = new HashSet<String>(); 371bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden static { 372bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // _id, _sync_account, _sync_account_type, dirty, _sync_mark, calendar_id 373bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events._SYNC_ID); 374bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA1); 375bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA7); 37602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA3); 377bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.TITLE); 378bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION); 379bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION); 3802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR); 381387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR_KEY); 382bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.STATUS); 383c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS); 38402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6); 385bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DTSTART); 386c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // dtend -- set from duration as part of creating the exception 387bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_TIMEZONE); 388bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_END_TIMEZONE); 389bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DURATION); 390bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ALL_DAY); 391bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ACCESS_LEVEL); 392bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.AVAILABILITY); 393bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ALARM); 394bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_EXTENDED_PROPERTIES); 395bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RRULE); 396bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RDATE); 397bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXRULE); 398bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXDATE); 399bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_SYNC_ID); 400bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_INSTANCE_TIME); 401bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // originalAllDay, lastDate 402bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ATTENDEE_DATA); 403bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_MODIFY); 404bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_INVITE_OTHERS); 405bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_SEE_GUESTS); 406bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORGANIZER); 407bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // deleted, original_id, alerts 408bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 409bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 410bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Don't clone these from the base event into the exception event. */ 411bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final String[] DONT_CLONE_INTO_EXCEPTION = { 412bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events._SYNC_ID, 413bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA1, 41402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA2, 41502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA3, 41602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA4, 41702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA5, 41802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA6, 419bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA7, 42002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA8, 421c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA9, 422c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA10, 423bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden }; 424bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 425bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** set to 'true' to enable debug logging for recurrence exception code */ 426bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final boolean DEBUG_EXCEPTION = false; 427bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 428dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 429e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 430e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 4318bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 4328bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 433420b7fb569773ae573fbe90c3a9c522d4c368863Erik @VisibleForTesting 434420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected CalendarAlarmManager mCalendarAlarm; 435a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 436a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 437a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 438a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 439dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 440a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 441a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 442a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 443dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 444dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 445dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 446dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 447dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 448a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 449a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 450a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 451a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 465420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 469420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 471420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4828bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4838bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4848bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4858bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 486e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 487e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 488e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 489e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 490e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 491e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 492e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 4938bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4948bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 4979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 498ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 499ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 500ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 501f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 502f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 503f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 504ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 505ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 506ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 508ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 5098bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 5108bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 511dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 512e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 513e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 514ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 515ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 5169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5174caf8d015918f619a67d321a152f150a01022717Andy McFadden mMetaData = new MetaData(mDbHelper); 5184caf8d015918f619a67d321a152f150a01022717Andy McFadden mInstancesHelper = new CalendarInstancesHelper(mDbHelper, mMetaData); 5194caf8d015918f619a67d321a152f150a01022717Andy McFadden 5209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 5219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 5229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 5249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 5259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 5269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 5289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 5299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 5309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 531e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 5329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 533ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 534ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 535420b7fb569773ae573fbe90c3a9c522d4c368863Erik // This is pulled out for testing 536420b7fb569773ae573fbe90c3a9c522d4c368863Erik initCalendarAlarm(); 537e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 538e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 5398bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 5419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 543420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected void initCalendarAlarm() { 544420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = getOrCreateCalendarAlarmManager(); 545420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.getScheduleNextAlarmWakeLock(); 546e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 547e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 548e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 549420b7fb569773ae573fbe90c3a9c522d4c368863Erik if (mCalendarAlarm == null) { 550420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = new CalendarAlarmManager(mContext); 551e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 552420b7fb569773ae573fbe90c3a9c522d4c368863Erik return mCalendarAlarm; 553e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 554e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 555ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 556ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 557ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 558ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 559ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 560ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 561ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 562ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 563ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 564ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 565ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 566ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 567ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 568ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 569ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 570ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 57164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void verifyAccounts() { 57264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 57364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(AccountManager.get(getContext()).getAccounts()); 57464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 57564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 57664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 5779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 580315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 5819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 5839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 5859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 5889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 5909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 591ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 5929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 596315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 597315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 598315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 599315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 600315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 601315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 602315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 603315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 6049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 6059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 6069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 607315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 608ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 609315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 610315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 611a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 612315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 613315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 614315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 615ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 616315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 617315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 618ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 619315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 620ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 621ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 622ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 623ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 624420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 625ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 626ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 627f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 628f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 629f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 630ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 631ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 632ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 633ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 634ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 635f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 636f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 637f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 638ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 6399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 640ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 641ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 642315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 643ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 644ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 6453443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 646ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 647315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 648ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 649ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 650ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 651ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 652ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 653ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 654ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 6553443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 6563443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 657ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 660ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 661ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6623443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 663f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 664f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 665f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 666f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 667f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 668f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 669f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6713443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 672ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 673ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 674ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 675ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 676ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 677ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 678ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 679ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 680ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 681ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 682ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 683f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 684f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 685f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 686ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 687ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 688ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 690ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 691ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 692f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 693f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 694f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 695ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 696ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 697ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 698ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 699ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 700ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 701ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 702ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 703ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 704b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 705b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 706ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 707b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 708dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 709ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 710ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 711ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 712ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 713ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 714ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 715ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 716ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 717ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 718ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 719ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 720f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 721f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 722f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 723ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 724ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 7259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 726ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 727ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 728ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 729ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 730315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 731315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 732ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 733ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 734ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 735ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 736ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 73725e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 738ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 739315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 740315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 741ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 742ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 743f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 744f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 745f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 746ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 747ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 748ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 749315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 750315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 751315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 752315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 753315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 754ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 7559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 7569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 7579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 7589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 759315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 760315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7661f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7691f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7701f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7711f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7721f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7731f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7741f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 7752ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* selection */, null, 7762ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* sort */, 777d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 778315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 7792ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik instancesTimezone, isHomeTimezone()); 7801f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 7811f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 7821f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 7831f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7841f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 786420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 7879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 791b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 7929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 7939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 794b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik mContentResolver.notifyChange(CalendarContract.CONTENT_URI, null, syncToNetwork); 7959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 7989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 800ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 801ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 8029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 8059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 809315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 8139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 814fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 816fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden case SYNCSTATE_ID: 817fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String selectionWithId = (SyncState._ID + "=?") 818fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden + (selection == null ? "" : " AND (" + selection + ")"); 819fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden // Prepend id to selectionArgs 820fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs = insertSelectionArg(selectionArgs, 821fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String.valueOf(ContentUris.parseId(uri))); 822fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selectionWithId, 823fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs, sortOrder); 8249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 8261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 8289ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 8299ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 8309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 8321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 834636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 835b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 83719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 83819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 83919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 84019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 8419ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 8429ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 84319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 84419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 84519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 84619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 847636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 848b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 84919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 85019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 8512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 8522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setTables(Tables.COLORS); 8532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setProjectionMap(sColorsProjectionMap); 8542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selection = appendAccountFromParameterToSelection(selection, uri); 8552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 8562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 8579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 85843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 859b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 8609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 8619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 86343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 864b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 865636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 866b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 8699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 8719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 8769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 8829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 884315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 8852ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceQuery(qb, begin, end, projection, selection, selectionArgs, 8862ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik sortOrder, match == INSTANCES_BY_DAY, false /* don't force an expansion */, 887315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 88881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 88981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 89081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 89181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 89281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 89381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 89481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 89581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 89681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 89781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 89881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 89981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 90081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 90181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 902315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 90381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 90481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 9052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceSearchQuery(qb, begin, end, query, projection, selection, 9062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 907315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9086db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 9099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 9109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 9119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 9159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 9169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 9199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 9219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 923315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 924315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 925315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 92702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 929ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan qb.appendWhere(SQL_WHERE_ATTENDEE_BASE); 9309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 93202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 934636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 935b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 9369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 938b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 9399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 94102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 943636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 944b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 947b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 949b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 952b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 954b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 958b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 960636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 961b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 9629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 964b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 967b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 968636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 969b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 9709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 971315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 972b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 973315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 974315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 9849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 9859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 986ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 98739c65e5716e21e863d8de587d139dae85f99422fFred Quintana if (projection != null && projection.length == 1 98839c65e5716e21e863d8de587d139dae85f99422fFred Quintana && BaseColumns._COUNT.equals(projection[0])) { 98939c65e5716e21e863d8de587d139dae85f99422fFred Quintana qb.setProjectionMap(sCountProjectionMap); 99039c65e5716e21e863d8de587d139dae85f99422fFred Quintana } 99139c65e5716e21e863d8de587d139dae85f99422fFred Quintana 992ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 993ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 994ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 995ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 996ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 997ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 998ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 999ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 10009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 10029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 10039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 1004b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik c.setNotificationUri(mContentResolver, CalendarContract.Events.CONTENT_URI); 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 10079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 10109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 10139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 10149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 10159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 10169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 10179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 10189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 10199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 1020d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1021315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1022315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 10239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 10249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 10262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik long rangeEnd, String[] projection, String selection, String[] selectionArgs, 10272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String sort, boolean searchByDay, boolean forceExpansion, 10282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 10299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 103081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 10319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 10329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 10339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 10349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 1035315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 10369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 10379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 10409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1041315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 1042315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1043b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 10449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 10459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1046315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 1047315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1048b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 10499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10502ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10512ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = new String[] {String.valueOf(rangeEnd), 10528335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 10532ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 10542ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 10552ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 10562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 10572ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 10582ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 10592ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10608335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 10617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 10629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 106481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 10652ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * Combine a set of arrays in the order they are passed in. All arrays must 10662ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * be of the same type. 10672ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 10682ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static <T> T[] combine(T[]... arrays) { 10692ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (arrays.length == 0) { 10702ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik throw new IllegalArgumentException("Must supply at least 1 array to combine"); 10712ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10722ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10732ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int totalSize = 0; 10742ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10752ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize += array.length; 10762ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10772ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10782ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik T[] finalArray = (T[]) (Array.newInstance(arrays[0].getClass().getComponentType(), 10792ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize)); 10802ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10812ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int currentPos = 0; 10822ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10832ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int length = array.length; 10842ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik System.arraycopy(array, 0, finalArray, currentPos, length); 10852ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik currentPos += array.length; 10862ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10872ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return finalArray; 10882ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10892ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10902ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 1091dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1092dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1093dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1094dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1095dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1096dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1097dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1098dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1099dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1100dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1101dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 110281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1103dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1104dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 110581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 110681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 110781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 110881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 110981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 111081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1111dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1112dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1113dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1114dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1115dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1116dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1117dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1118dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1119dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1120dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1121dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1122dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1123dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1124dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 112581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 112681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 112781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 112881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 112981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 113081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 113181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 113281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 113381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 113481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 113581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 113681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 113781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 113881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 113981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 114081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 114181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 114281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 114381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 1144cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * approximation of full-text search across multiple columns. The set 1145cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * of columns is specified by the SEARCH_COLUMNS constant. 1146cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * <p> 1147cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * Note the "WHERE" token isn't part of the returned string. The value 1148cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * may be passed into a query as the "HAVING" clause. 114981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 115081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 115181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 115281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 115381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 115481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 115681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 115781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 115881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 115981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 116081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1161dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1162dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1163dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 116481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 116581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 116681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 116781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 116818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 116918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 117018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 117118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 117281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 117381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 117481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 117581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 117681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 117781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 117818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 117918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 118081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 118118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 118218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 118318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 118481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1185f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1186f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 118718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 118881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 118981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 119181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 119381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 119481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 11952ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selection, String[] selectionArgs, String sort, boolean searchByDay, 11962ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 119718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 119881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 119981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1200dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 12012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 12022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 12032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 12042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 12052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 12062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 12072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 12082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 120918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 121018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1211dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1212dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 121381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 121481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 121581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1216315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 121781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 121881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 121981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 122081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 122181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 122218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 122318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 122456292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 122556292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1226315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1227315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1228315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 122956292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1230b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 123181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 123281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 123318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 123418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 123556292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 123656292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1237315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1238315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1239315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 124056292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1241b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 124281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 124381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 124418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 1245c3780839fd044b5d8109860b57a199a2da1d804fMichael Chan Tables.INSTANCES + "." + Instances._ID /* groupBy */, 1246cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden searchWhere /* having */, sort); 124781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 124881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 12496db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1250315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1251315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 125281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 12536db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 125443556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 125543556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1256315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1257192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 125843556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 125943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1260192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 126143556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1264b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 12658335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 12668335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 12678335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 12686db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12739ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * table. Acquires the database lock and calls 12749ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #acquireInstanceRangeLocked(long, long, boolean, boolean, String, boolean)}. 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1279d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1280315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1281315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1283d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1285315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 12869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 12879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1289315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 12909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 12919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 12929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 12939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 12999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 13029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1303315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1304315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1305315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1307420b7fb569773ae573fbe90c3a9c522d4c368863Erik void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1308315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 13099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 13119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1312d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1313d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "acquireInstanceRange begin=" + begin + " end=" + end + 1314d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " useMin=" + useMinimumExpansionWindow + " force=" + forceExpansion); 1315d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1316d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1317315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1318315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1319315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1320315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1321315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 13399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1340315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1341315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1342315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1343315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1344315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1345315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1346315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 13477be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 13487be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 13497be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 13507be45683e367bd6897daf6444b03be938f8f5eaaErik } 1351315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1352315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1353315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1354d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 1355d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1356d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "Wiping instances and expanding from scratch"); 1357d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1358d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 13599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1360b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1361f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13626db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 13649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1365f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1366315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1367315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 13689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1369315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 13707be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 13717be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 13727be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 13737be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 13747be45683e367bd6897daf6444b03be938f8f5eaaErik 13757be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1376315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 13777be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 13787be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 13797be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 13807be45683e367bd6897daf6444b03be938f8f5eaaErik } 1381315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 13879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 13919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 13929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 13939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1397d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1398d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "instances are already expanded"); 1399d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1400f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 14019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 14029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 14039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 14059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 14089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 14099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1410f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 14119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 14129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 14169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1417f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 14189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1422315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 14239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 14269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 14279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 14289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 14299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 14309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 14319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 14329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 14456db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 144748587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 144848587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1449315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1450315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 14519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1456b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /** 1457b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Determines if the event is recurrent, based on the provided values. 1458b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1459b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden public static boolean isRecurrenceEvent(String rrule, String rdate, String originalId, 1460b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId) { 1461b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return (!TextUtils.isEmpty(rrule) || 1462b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(rdate) || 1463b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalId) || 1464b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalSyncId)); 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1467646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1468646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1469d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 1470646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1471d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * corrects the fields DTSTART, DTEND, and DURATION if necessary. 1472646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1473d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values The values to check and correct 1474d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param modValues Any updates will be stored here. This may be the same object as 1475d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <strong>values</strong>. 1476646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1477646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1478d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean fixAllDayTime(ContentValues values, ContentValues modValues) { 1479499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden Integer allDayObj = values.getAsInteger(Events.ALL_DAY); 1480499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden if (allDayObj == null || allDayObj == 0) { 1481d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return false; 1482d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1483d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1484646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1485646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1486d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtstart = values.getAsLong(Events.DTSTART); 1487d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtend = values.getAsLong(Events.DTEND); 1488d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String duration = values.getAsString(Events.DURATION); 1489d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Time time = new Time(); 1490d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String tempValue; 1491d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1492d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Change dtstart so h,m,s are 0 if necessary. 1493d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.clear(Time.TIMEZONE_UTC); 1494d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtstart.longValue()); 1495d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1496d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.hour = 0; 1497d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.minute = 0; 1498d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.second = 0; 1499d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTSTART, time.toMillis(true)); 1500d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1501d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1502d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1503d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If dtend exists for this event make sure it's h,m,s are 0. 1504d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (dtend != null) { 1505646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1506d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtend.longValue()); 1507646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1508646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1509646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1510646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1511d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden dtend = time.toMillis(true); 1512d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTEND, dtend); 1513646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1514646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1515d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1516646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1517d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (duration != null) { 1518d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int len = duration.length(); 1519d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1520d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * in the seconds format, and if so converts it to days. 1521d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 1522d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (len == 0) { 1523d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = null; 1524d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else if (duration.charAt(0) == 'P' && 1525d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration.charAt(len - 1) == 'S') { 1526d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1527d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1528d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = "P" + days + "D"; 1529d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DURATION, duration); 1530d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1531646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1532646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1533d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1534646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1535646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1536646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1537bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1538bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1539bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Determines whether the strings in the set name columns that may be overridden 1540bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * when creating a recurring event exception. 1541bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1542bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This uses a white list because it screens out unknown columns and is a bit safer to 1543bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * maintain than a black list. 1544bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1545bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private void checkAllowedInException(Set<String> keys) { 1546bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : keys) { 1547bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!ALLOWED_IN_EXCEPTION.contains(str.intern())) { 1548bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions can't overwrite " + str); 1549bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1550bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1551bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1552bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1553bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 155432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Splits a recurrent event at a specified instance. This is useful when modifying "this 155532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * and all future events". 155632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 155732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence rule has a COUNT specified, we need to split that at the point of the 155832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * exception. If the exception is instance N (0-based), the original COUNT is reduced 155932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * to N, and the exception's COUNT is set to (COUNT - N). 156032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 156132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence doesn't have a COUNT, we need to update or introduce an UNTIL value, 156232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the original recurrence will end just before the exception instance. (Note 156332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * that UNTIL dates are inclusive.) 156432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 156532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * This should not be used to update the first instance ("update all events" action). 1566bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 156732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @param values The original event values; must include EVENT_TIMEZONE and DTSTART. 156832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * The RRULE value may be modified (with the expectation that this will propagate 156932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * into the exception event). 1570bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param endTimeMillis The time before which the event must end (i.e. the start time of the 1571bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event instance). 157232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @return Values to apply to the original event. 1573bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1574bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static ContentValues setRecurrenceEnd(ContentValues values, long endTimeMillis) { 157532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden boolean origAllDay = values.getAsBoolean(Events.ALL_DAY); 157632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden String origRrule = values.getAsString(Events.RRULE); 1577bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 157832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence origRecurrence = new EventRecurrence(); 157932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.parse(origRrule); 1580bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 158132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Get the start time of the first instance in the original recurrence. 158232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long startTimeMillis = values.getAsLong(Events.DTSTART); 1583bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time dtstart = new Time(); 1584bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = values.getAsString(Events.EVENT_TIMEZONE); 158532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.set(startTimeMillis); 1586bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1587bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues updateValues = new ContentValues(); 158832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 158932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origRecurrence.count > 0) { 159032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden /* 159132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Generate the full set of instances for this recurrence, from the first to the 159232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * one just before endTimeMillis. The list should never be empty, because this method 159332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * should not be called for the first instance. All we're really interested in is 159432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * the *number* of instances found. 159532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden */ 159632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceSet recurSet = new RecurrenceSet(values); 159732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceProcessor recurProc = new RecurrenceProcessor(); 159832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long[] recurrences; 159932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden try { 160032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden recurrences = recurProc.expand(dtstart, recurSet, startTimeMillis, endTimeMillis); 160132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } catch (DateException de) { 160232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException(de); 160332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 160432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 160532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (recurrences.length == 0) { 160632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException("can't use this method on first instance"); 160732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 160832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 160932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence excepRecurrence = new EventRecurrence(); 16101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden excepRecurrence.parse(origRrule); // TODO: add/use a copy constructor to EventRecurrence 161132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden excepRecurrence.count -= recurrences.length; 161232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden values.put(Events.RRULE, excepRecurrence.toString()); 161332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 161432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.count = recurrences.length; 161532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 161632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } else { 161732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden Time untilTime = new Time(); 161832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 161932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // The "until" time must be in UTC time in order for Google calendar 162032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // to display it properly. For all-day events, the "until" time string 162132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // must include just the date field, and not the time field. The 162232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // repeating events repeat up to and including the "until" time. 162332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.timezone = Time.TIMEZONE_UTC; 162432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 162532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Subtract one second from the exception begin time to get the "until" time. 162632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.set(endTimeMillis - 1000); // subtract one second (1000 millis) 162732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origAllDay) { 162832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.hour = untilTime.minute = untilTime.second = 0; 162932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.allDay = true; 163032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.normalize(false); 163132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 163232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // This should no longer be necessary -- DTSTART should already be in the correct 163332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // format for an all-day event. 163432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.hour = dtstart.minute = dtstart.second = 0; 163532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.allDay = true; 163632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.timezone = Time.TIMEZONE_UTC; 163732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 163832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.until = untilTime.format2445(); 163932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 164032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 164132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden updateValues.put(Events.RRULE, origRecurrence.toString()); 1642bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.DTSTART, dtstart.normalize(true)); 1643bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return updateValues; 1644bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1645bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1646bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1647bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Handles insertion of an exception to a recurring event. 1648bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1649bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * There are two modes, selected based on the presence of "rrule" in modValues: 1650bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <ol> 1651bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Create a single instance exception ("modify current event only"). 1652bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Cap the original event, and create a new recurring event ("modify this and all 1653bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * future events"). 1654bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * </ol> 1655bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This may be used for "modify all instances of the event" by simply selecting the 1656bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * very first instance as the exception target. In that case, the ID of the "new" 1657bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event will be the same as the originalEventId. 1658bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1659bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param originalEventId The _id of the event to be modified 1660bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param modValues Event columns to update 1661c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * @param callerIsSyncAdapter Set if the content provider client is the sync adapter 1662bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return the ID of the new "exception" event, or -1 on failure 1663bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1664c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden private long handleInsertException(long originalEventId, ContentValues modValues, 1665c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden boolean callerIsSyncAdapter) { 1666bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1667bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.i(TAG, "RE: values: " + modValues.toString()); 1668bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1669bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1670bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Make sure they have specified an instance via originalInstanceTime. 1671bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Long originalInstanceTime = modValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1672bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime == null) { 1673bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions must specify " + 1674bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.ORIGINAL_INSTANCE_TIME); 1675bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1676bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1677bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Check for attempts to override values that shouldn't be touched. 1678bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden checkAllowedInException(modValues.keySet()); 1679bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1680c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // If this isn't the sync adapter, set the "dirty" flag in any Event we modify. 1681c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!callerIsSyncAdapter) { 1682c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.put(Events.DIRTY, true); 1683c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1684c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1685bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Wrap all database accesses in a transaction. 1686bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.beginTransaction(); 1687bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Cursor cursor = null; 1688bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1689bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that there's an instance corresponding to the specified time 1690bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (does this matter? it's weird, but not fatal?) 1691bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1692bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Grab the full set of columns for this event. 1693bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor = mDb.query(Tables.EVENTS, null /* columns */, 1694bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(originalEventId) }, 1695bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1696bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor.getCount() != 1) { 1697bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event ID " + originalEventId + " lookup failed (count is " + 1698bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.getCount() + ")"); 1699bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1700bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1701bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden //DatabaseUtils.dumpCursor(cursor); 1702bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 17032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If there's a color index check that it's valid 1704387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_index = modValues.getAsString(Events.EVENT_COLOR_KEY); 17052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_index)) { 17062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int calIdCol = cursor.getColumnIndex(Events.CALENDAR_ID); 17072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calId = cursor.getLong(calIdCol); 17082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 17092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 17102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calId != null) { 17112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calId); 17122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 17132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 17142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 17152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_index, Colors.TYPE_EVENT); 17182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1720bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1721bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Verify that the original event is in fact a recurring event by checking for the 1722bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * presence of an RRULE. If it's there, we assume that the event is otherwise 1723bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * properly constructed (e.g. no DTEND). 1724bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1725bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.moveToFirst(); 1726bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int rruleCol = cursor.getColumnIndex(Events.RRULE); 1727bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (TextUtils.isEmpty(cursor.getString(rruleCol))) { 1728bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event has no rrule"); 1729bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1730bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1731bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1732bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: old RRULE is " + cursor.getString(rruleCol)); 1733bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1734bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1735bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Verify that the original event is not itself a (single-instance) exception. 1736bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int originalIdCol = cursor.getColumnIndex(Events.ORIGINAL_ID); 1737bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!TextUtils.isEmpty(cursor.getString(originalIdCol))) { 1738bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event is an exception"); 1739bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1740bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1741bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1742bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createSingleException = TextUtils.isEmpty(modValues.getAsString(Events.RRULE)); 1743bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1744bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: check for the presence of an existing exception on this event+instance? 1745bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The caller should be modifying that, not creating another exception. 1746bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Alternatively, we could do that for them.) 1747bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1748bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Create a new ContentValues for the new event. Start with the original event, 1749bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // and drop in the new caller-supplied values. This will set originalInstanceTime. 1750bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues values = new ContentValues(); 1751bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 1752f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor.close(); 1753f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor = null; 1754bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1755b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: if we're changing this to an all-day event, we should ensure that 1756b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // hours/mins/secs on DTSTART are zeroed out (before computing DTEND). 1757b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // See fixAllDayTime(). 1758b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1759bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createNewEvent = true; 1760bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createSingleException) { 1761bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1762bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Save a copy of a few fields that will migrate to new places. 1763bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1764bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _id = values.getAsString(Events._ID); 1765bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _sync_id = values.getAsString(Events._SYNC_ID); 1766bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1767bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1768bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1769bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Wipe out some fields that we don't want to clone into the exception event. 1770bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1771bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : DONT_CLONE_INTO_EXCEPTION) { 1772bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(str); 1773bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1774bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1775bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1776bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Merge the new values on top of the existing values. Note this sets 1777bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * originalInstanceTime. 1778bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1779bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1780bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1781bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1782bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Copy some fields to their "original" counterparts: 1783bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id --> original_id 1784bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _sync_id --> original_sync_id 1785bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * allDay --> originalAllDay 1786bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1787bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this event hasn't been sync'ed with the server yet, the _sync_id field will 1788bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * be null. We will need to fill original_sync_id in later. (May not be able to 1789bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * do it right when our own _sync_id field gets populated, because the order of 1790bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * events from the server may not be what we want -- could update the exception 1791bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * before updating the original event.) 1792bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1793bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id is removed later (right before we write the event). 1794bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1795bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ID, _id); 1796bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_SYNC_ID, _sync_id); 1797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ALL_DAY, allDay); 1798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Mark the exception event status as "tentative", unless the caller has some 1800bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // other value in mind (like STATUS_CANCELED). 1801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!values.containsKey(Events.STATUS)) { 1802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.STATUS, Events.STATUS_TENTATIVE); 1803bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1804bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1805bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // We're converting from recurring to non-recurring. Clear out RRULE and replace 1806bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DURATION with DTEND. 1807c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.remove(Events.RRULE); 1808bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1809bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Duration duration = new Duration(); 1810bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String durationStr = values.getAsString(Events.DURATION); 1811bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1812bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden duration.parse(durationStr); 1813bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } catch (Exception ex) { 1814bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // NullPointerException if the original event had no duration. 1815bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DateException if the duration was malformed. 1816bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Bad duration in recurring event: " + durationStr, ex); 1817bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1818bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1819bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1820c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1821c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * We want to compute DTEND as an offset from the start time of the instance. 1822c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If the caller specified a new value for DTSTART, we want to use that; if not, 1823c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the DTSTART in "values" will be the start time of the first instance in the 1824c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * recurrence, so we want to replace it with ORIGINAL_INSTANCE_TIME. 1825c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1826c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long start; 1827c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.DTSTART)) { 1828c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.DTSTART); 1829c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1830c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1831c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.put(Events.DTSTART, start); 1832c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1833bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.DTEND, start + duration.getMillis()); 1834bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1835c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "RE: ORIG_INST_TIME=" + start + 1836c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ", duration=" + duration.getMillis() + 1837bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ", generated DTEND=" + values.getAsLong(Events.DTEND)); 1838bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 183985c09a31bcc3a18e173428bf7b628cec2834bebcAndy McFadden values.remove(Events.DURATION); 1840bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1841bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1842bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're going to "split" the recurring event, making the old one stop before 1843bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * this instance, and creating a new recurring event that starts here. 1844bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1845bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * No need to fill out the "original" fields -- the new event is not tied to 1846bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * the previous event in any way. 1847bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1848bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this is the first event in the series, we can just update the existing 1849bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event with the values. 1850bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1851bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean canceling = (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 1852bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1853bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime.equals(values.getAsLong(Events.DTSTART))) { 1854bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1855bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Update fields in the existing event. Rather than use the merged data 1856bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * from the cursor, we just do the update with the new value set after 1857bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * removing the ORIGINAL_INSTANCE_TIME entry. 1858bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1859bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (canceling) { 1860bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: should we just call deleteEventInternal? 1861bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "Note: canceling entire event via exception call"); 1862bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1863bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1864bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: updating full event"); 1865bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1866ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(modValues)) { 1867ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 1868ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 1869ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 1870bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden modValues.remove(Events.ORIGINAL_INSTANCE_TIME); 1871bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 1872bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1873bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden createNewEvent = false; // skip event creation and related-table cloning 1874bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1875bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1876bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: splitting event"); 1877bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1878bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1879bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 188032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Cap the original event so it ends just before the target instance. In 188132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * some cases (nonzero COUNT) this will also update the RRULE in "values", 188232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the exception we're creating terminates appropriately. If a 188332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * new RRULE was specified by the caller, the new rule will overwrite our 188432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * changes when we merge the new values in below (which is the desired 188532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * behavior). 1886bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1887bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues splitValues = setRecurrenceEnd(values, originalInstanceTime); 1888bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, splitValues, SQL_WHERE_ID, 1889bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1890bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1891bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 189232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Prepare the new event. We remove originalInstanceTime, because we're now 1893bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * creating a new event rather than an exception. 1894bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1895bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're always cloning a non-exception event (we tested to make sure the 1896bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event doesn't specify original_id, and we don't allow original_id in the 1897bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * modValues), so we shouldn't end up creating a new event that looks like 1898bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * an exception. 1899bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1900bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1901bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events.ORIGINAL_INSTANCE_TIME); 1902bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1903c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1904bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1905bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long newEventId; 1906bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createNewEvent) { 1907bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events._ID); // don't try to set this explicitly 1908be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 1909be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, null); 1910be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 1911be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 1912be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 1913bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1914bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = mDb.insert(Tables.EVENTS, null, values); 1915bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (newEventId < 0) { 1916bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Unable to add exception to recurring event"); 1917bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Values: " + values); 1918bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1919bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1920bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1921bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: new ID is " + newEventId); 1922bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1923bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1924b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: do we need to do something like this? 1925b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden //updateEventRawTimesLocked(id, updatedValues); 1926b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1927b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1928b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Force re-computation of the Instances associated with the recurrence event. 1929b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1930b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, newEventId, true, mDb); 1931b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1932bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1933bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Some of the other tables (Attendees, Reminders, ExtendedProperties) reference 1934c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Event ID. We need to copy the entries from the old event, filling in the 1935c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * new event ID, so that somebody doing a SELECT on those tables will find 1936c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * matching entries. 1937bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1938bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden CalendarDatabaseHelper.copyEventRelatedTables(mDb, newEventId, originalEventId); 1939c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1940c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1941c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If we modified Event.selfAttendeeStatus, we need to keep the corresponding 1942c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * entry in the Attendees table in sync. 1943c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1944c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 1945c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1946c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * Each Attendee is identified by email address. To find the entry that 1947c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * corresponds to "self", we want to compare that address to the owner of 1948c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Calendar. We're expecting to find one matching entry in Attendees. 1949c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1950c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long calendarId = values.getAsLong(Events.CALENDAR_ID); 1951f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden String accountName = getOwner(calendarId); 1952f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden 1953f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden if (accountName != null) { 1954c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ContentValues attValues = new ContentValues(); 1955c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.put(Attendees.ATTENDEE_STATUS, 1956c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.getAsString(Events.SELF_ATTENDEE_STATUS)); 1957c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1958c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (DEBUG_EXCEPTION) { 1959c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "Updating attendee status for event=" + newEventId + 1960c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden " name=" + accountName + " to " + 1961c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.getAsString(Attendees.ATTENDEE_STATUS)); 1962c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1963c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden int count = mDb.update(Tables.ATTENDEES, attValues, 1964c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Attendees.EVENT_ID + "=? AND " + Attendees.ATTENDEE_EMAIL + "=?", 1965c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden new String[] { String.valueOf(newEventId), accountName }); 1966b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (count != 1 && count != 2) { 1967b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // We're only expecting one matching entry. We might briefly see 1968b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // two during a server sync. 19697148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik Log.e(TAG, "Attendee status update on event=" + newEventId 19707148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " touched " + count + " rows. Expected one or two rows."); 1971b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (false) { 1972b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This dumps PII in the log, don't ship with it enabled. 1973b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Cursor debugCursor = mDb.query(Tables.ATTENDEES, null, 1974b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.EVENT_ID + "=? AND " + 1975b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.ATTENDEE_EMAIL + "=?", 1976b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { String.valueOf(newEventId), accountName }, 1977b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden null, null, null); 1978b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden DatabaseUtils.dumpCursor(debugCursor); 1979b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden } 1980b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden throw new RuntimeException("Status update WTF"); 1981c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1982c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1983c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1984bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1985b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1986b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Update any Instances changed by the update to this Event. 1987b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1988b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, originalEventId, false, mDb); 1989bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = originalEventId; 1990bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1991bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1992bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.setTransactionSuccessful(); 1993bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return newEventId; 1994bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } finally { 1995bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor != null) { 1996bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.close(); 1997bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1998bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.endTransaction(); 1999bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2000bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2001bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 2002222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden /** 2003222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Fills in the originalId column for previously-created exceptions to this event. If 2004222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this event is not recurring or does not have a _sync_id, this does nothing. 2005222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2006222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * The server might send exceptions before the event they refer to. When 2007222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this happens, the originalId field will not have been set in the 2008222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * exception events (it's the recurrence events' _id field, so it can't be 2009222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * known until the recurrence event is created). When we add a recurrence 2010222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * event with a non-empty _sync_id field, we write that event's _id to the 2011222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * originalId field of any events whose originalSyncId matches _sync_id. 2012222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2013222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Note _sync_id is only expected to be unique within a particular calendar. 2014222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * 2015222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param id The ID of the Event 2016222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param values Values for the Event being inserted 2017222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden */ 2018222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden private void backfillExceptionOriginalIds(long id, ContentValues values) { 2019222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String syncId = values.getAsString(Events._SYNC_ID); 2020222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rrule = values.getAsString(Events.RRULE); 2021222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rdate = values.getAsString(Events.RDATE); 2022222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String calendarId = values.getAsString(Events.CALENDAR_ID); 2023222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2024222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden if (TextUtils.isEmpty(syncId) || TextUtils.isEmpty(calendarId) || 2025222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden (TextUtils.isEmpty(rrule) && TextUtils.isEmpty(rdate))) { 2026222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden // Not a recurring event, or doesn't have a server-provided sync ID. 2027222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden return; 2028222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2029222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2030222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden ContentValues originalValues = new ContentValues(); 2031222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden originalValues.put(Events.ORIGINAL_ID, id); 2032222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden mDb.update(Tables.EVENTS, originalValues, 2033222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden Events.ORIGINAL_SYNC_ID + "=? AND " + Events.CALENDAR_ID + "=?", 2034222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden new String[] { syncId, calendarId }); 2035222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2036222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 20379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2038b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 2039ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 20419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20420739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 20430739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_INSERT, uri, values, callerIsSyncAdapter, match, 20440739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik null /* selection */, null /* selection args */); 20459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 20479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 2049bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case SYNCSTATE: 20509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 20519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 20529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 20537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2054c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 20557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 20569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 20578253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert if (values.containsKey(Events.ORIGINAL_SYNC_ID) 20588253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && values.containsKey(Events.ORIGINAL_INSTANCE_TIME) 20598253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && Events.STATUS_CANCELED == values.getAsInteger(Events.STATUS)) { 20608253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // event is a canceled instance of a recurring event, it doesn't these 20618253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // values but lets fake some to satisfy curious consumers. 20628253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert final long origStart = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 20638253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTSTART, origStart); 20648253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTEND, origStart); 20658253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.EVENT_TIMEZONE, Time.TIMEZONE_UTC); 20668253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } else { 20678253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert throw new RuntimeException("DTSTART field missing from event"); 20688253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } 20699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 2071e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 2072be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 2073be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(updatedValues, null); 2074be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 2075be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(updatedValues); 2076be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 2077e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 2078e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 20799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 20809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 20819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 20829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calendar_id = updatedValues.getAsLong(Events.CALENDAR_ID); 20842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendar_id == null) { 20852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // validateEventData checks this for non-sync adapter 20862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // inserts 20872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("New events must specify a calendar id"); 20882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 20892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color is valid if it is being set 2090387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = updatedValues.getAsString(Events.EVENT_COLOR_KEY); 20912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 20922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calendar_id); 20932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 20942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 20952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 20962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 20972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 20982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 20992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, color_id, 21002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_EVENT); 21012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik updatedValues.put(Events.EVENT_COLOR, color); 21022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 21042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!updatedValues.containsKey(Events.ORGANIZER)) { 21052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 21069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 21099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 21109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 211434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 211534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !updatedValues.containsKey(Events.ORIGINAL_ID)) { 211634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = getOriginalId(updatedValues 211734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsString(Events.ORIGINAL_SYNC_ID)); 211834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId != -1) { 211934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_ID, originalId); 212034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 212134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } else if (!updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 212234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && updatedValues.containsKey(Events.ORIGINAL_ID)) { 212334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = getOriginalSyncId(updatedValues 212434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsLong(Events.ORIGINAL_ID)); 212534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (!TextUtils.isEmpty(originalSyncId)) { 212634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_SYNC_ID, originalSyncId); 212734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 212834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 2129d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(updatedValues, updatedValues)) { 2130f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2131f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 2132f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 2133f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2134646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 21351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updatedValues.remove(Events.HAS_ALARM); // should not be set by caller 2136c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 21379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 21389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 2140f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 2141f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik true /* new event */, mDb); 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 21449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 21459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 21469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 21479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 21482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 21499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 21519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2152b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 2153222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden backfillExceptionOriginalIds(id, values); 2154222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2155dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2158bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID: 2159bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long originalEventId = ContentUris.parseId(uri); 2160c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden id = handleInsertException(originalEventId, values, callerIsSyncAdapter); 2161bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden break; 21629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 216382b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden // TODO: verify that all required fields are present 21649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 21659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 2166c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 21679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 2168c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 21699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 2170fa332ecedc0c340109811552407142f6e4f600b2RoboErik String eventsUrl = values.getAsString(Calendars.CAL_SYNC1); 21711b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 21729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2173387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String cal_color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 21742f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(cal_color_id)) { 21752f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 21762f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 21772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, cal_color_id, 21782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_CALENDAR); 21792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Calendars.CALENDAR_COLOR, color); 21802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2182dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 21852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // verifyTransactionAllowed requires this be from a sync 21862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter, all of the required fields are marked NOT NULL in 21872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // the db. TODO Do we need explicit checks here or should we 21882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // just let sqlite throw if something isn't specified? 21892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = uri.getQueryParameter(Colors.ACCOUNT_NAME); 21902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = uri.getQueryParameter(Colors.ACCOUNT_TYPE); 2191387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String colorIndex = values.getAsString(Colors.COLOR_KEY); 21922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 21932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Account name and type must be non" 21942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " empty parameters for " + uri); 21952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(colorIndex)) { 21972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("COLOR_INDEX must be non empty for " + uri); 21982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!values.containsKey(Colors.COLOR_TYPE) || !values.containsKey(Colors.COLOR)) { 22002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException( 22012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik "New colors must contain COLOR_TYPE and COLOR"); 22022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Make sure the account we're inserting for is the same one the 22042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter is claiming to be. TODO should we throw if they 22052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // aren't the same? 22062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_NAME, accountName); 22072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_TYPE, accountType); 22082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 22092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color doesn't already exist 22102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 22112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 22124755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan final long colorType = values.getAsLong(Colors.COLOR_TYPE); 22134755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 22142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.getCount() != 0) { 22154755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("color type " + colorType 22164755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + " and index " + colorIndex 22177148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " already exists for account and type provided"); 22182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 22202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) 22212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 22222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik id = mDbHelper.colorsInsert(values); 22242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 22259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 22269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 22279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 22289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 22299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 22319ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Attendees.EVENT_ID); 22329ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 22339ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 22347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22359ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.attendeesInsert(values); 22369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 22389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 22399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 22411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden { 22421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 22431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventIdObj == null) { 22449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 22451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "contain a numeric event_id"); 22469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 22481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventIdObj); 22491c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setEventDirty(eventIdObj); 22507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22519ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.remindersInsert(values); 22529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // We know this event has at least one reminder, so make sure "hasAlarm" is 1. 22541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setHasAlarm(eventIdObj, 1); 22551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 22569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 22579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 22589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 22599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2260420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 22619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 22639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 22649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 22659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 22669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 22679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 22692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 22702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 22719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 2273b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (!values.containsKey(CalendarContract.ExtendedProperties.EVENT_ID)) { 22749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 22759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 22769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22777e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2278b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik final Long eventId = values 2279b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik .getAsLong(CalendarContract.ExtendedProperties.EVENT_ID); 22809ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 22819ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 22827e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 22839ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.extendedPropertiesInsert(values); 22849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22853b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden case EMMA: 22863b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Special target used during code-coverage evaluation. 22873b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden handleEmmaRequest(values); 22883b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden break; 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 22939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 22949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 22956db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2296315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 22977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 22989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 22999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 23009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 23039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2309e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 23103b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Handles special commands related to EMMA code-coverage testing. 23113b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 23123b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * @param values Parameters from the caller. 23133b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 23143b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static void handleEmmaRequest(ContentValues values) { 23153b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /* 23163b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * This is not part of the public API, so we can't share constants with the CTS 23173b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * test code. 23183b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 23193b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Bad requests, or attempting to request EMMA coverage data when the coverage libs 23203b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * aren't linked in, will cause an exception. 23213b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 23223b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String cmd = values.getAsString("cmd"); 23233b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden if (cmd.equals("start")) { 23243b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // We'd like to reset the coverage data, but according to FAQ item 3.14 at 23253b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // http://emma.sourceforge.net/faq.html, this isn't possible in 2.0. 23263b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage testing started"); 23273b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } else if (cmd.equals("stop")) { 23283b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Call com.vladium.emma.rt.RT.dumpCoverageData() to cause a data dump. We 23293b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // may not have been built with EMMA, so we need to do this through reflection. 23303b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String filename = values.getAsString("outputFileName"); 23313b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23323b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden File coverageFile = new File(filename); 23333b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden try { 23343b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); 23353b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", 23363b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden coverageFile.getClass(), boolean.class, boolean.class); 23373b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23383b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden dumpCoverageMethod.invoke(null, coverageFile, false /*merge*/, 23393b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden false /*stopDataCollection*/); 23403b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage data written to " + filename); 23413b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } catch (Exception e) { 23423b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden throw new RuntimeException("Emma coverage dump failed", e); 23433b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23443b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23453b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 23463b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 23473b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /** 23485ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * Validates the recurrence rule, if any. We allow single- and multi-rule RRULEs. 2349ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * <p> 23505ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * TODO: Validate RDATE, EXRULE, EXDATE (possibly passing in an indication of whether we 23515ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * believe we have the full set, so we can reject EXRULE when not accompanied by RRULE). 2352ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * 2353ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * @return A boolean indicating successful validation. 2354ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden */ 2355ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden private boolean validateRecurrenceRule(ContentValues values) { 2356ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden String rrule = values.getAsString(Events.RRULE); 2357ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2358ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!TextUtils.isEmpty(rrule)) { 23595ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden String[] ruleList = rrule.split("\n"); 23605ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden for (String recur : ruleList) { 23615ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden EventRecurrence er = new EventRecurrence(); 23625ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden try { 23635ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden er.parse(recur); 23645ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } catch (EventRecurrence.InvalidFormatException ife) { 23655ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden Log.w(TAG, "Invalid recurrence rule: " + recur); 2366bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 23675ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden return false; 23685ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } 2369ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2370ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2371ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2372ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden return true; 2373ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2374ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2375bfea6da707f8d352432096371e7da76c230d9059Michael Chan private void dumpEventNoPII(ContentValues values) { 2376bfea6da707f8d352432096371e7da76c230d9059Michael Chan if (values == null) { 2377bfea6da707f8d352432096371e7da76c230d9059Michael Chan return; 2378bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2379bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2380bfea6da707f8d352432096371e7da76c230d9059Michael Chan StringBuilder bob = new StringBuilder(); 2381bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("dtStart: ").append(values.getAsLong(Events.DTSTART)); 2382bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndtEnd: ").append(values.getAsLong(Events.DTEND)); 2383bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nall_day: ").append(values.getAsInteger(Events.ALL_DAY)); 2384bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ntz: ").append(values.getAsString(Events.EVENT_TIMEZONE)); 2385bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndur: ").append(values.getAsString(Events.DURATION)); 2386bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrrule: ").append(values.getAsString(Events.RRULE)); 2387bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrdate: ").append(values.getAsString(Events.RDATE)); 2388bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nlast_date: ").append(values.getAsLong(Events.LAST_DATE)); 2389bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2390bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nid: ").append(values.getAsLong(Events._ID)); 2391bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nsync_id: ").append(values.getAsString(Events._SYNC_ID)); 2392bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_id: ").append(values.getAsLong(Events.ORIGINAL_ID)); 2393bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_sync_id: ").append(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2394bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_inst_time: ").append(values.getAsLong(Events.ORIGINAL_INSTANCE_TIME)); 2395bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_all_day: ").append(values.getAsInteger(Events.ORIGINAL_ALL_DAY)); 2396bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2397bfea6da707f8d352432096371e7da76c230d9059Michael Chan Log.i(TAG, bob.toString()); 2398bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2399bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2400ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden /** 240162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Do some scrubbing on event data before inserting or updating. In particular make 240262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * dtend, duration, etc make sense for the type of event (regular, recurrence, exception). 240362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Remove any unexpected fields. 2404e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 240562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param values the ContentValues to insert. 240662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param modValues if non-null, explicit null entries will be added here whenever something 240762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * is removed from <strong>values</strong>. 2408e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 240962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden private void scrubEventData(ContentValues values, ContentValues modValues) { 2410e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2411e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2412e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2413e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2414c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2415e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2416e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2417e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2418e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2419e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2420e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2421ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden // rrule is a valid recurrence rule 2422e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2423e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2424e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2425ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(values)) { 2426ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2427ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 2428ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2429e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 243062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DTEND, ORIGINAL_SYNC_ID, ORIGINAL_INSTANCE_TIME"); 2431e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 243262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence: " + values); 2433e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2434e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2435c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.remove(Events.ORIGINAL_SYNC_ID); 2436e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 243762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 243862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DTEND); 243962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_SYNC_ID); 244062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_INSTANCE_TIME); 244162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2442e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2443e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2444e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2445e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2446e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2447e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2448e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2449e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2450e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2451e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2452e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 245362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2454e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 245562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence exception: " + values); 2456e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2457e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 245862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 245962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 246062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2461e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2462e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2463e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2464e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2465e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2466e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2467e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2468e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2469e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2470e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2471e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 247262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2473e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 247462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for event: " + values); 2475e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2476e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 247762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 247862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 247962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2480e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2481e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2482e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2483e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 2484d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2485d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Validates event data. Pass in the full set of values for the event (i.e. not just 2486d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * a part that's being updated). 2487d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2488d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values Event data. 2489d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws IllegalArgumentException if bad data is found. 2490d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 2491d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private void validateEventData(ContentValues values) { 249282b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.CALENDAR_ID))) { 249382b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include a calendar_id"); 249482b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 249582b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.EVENT_TIMEZONE))) { 249682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include an eventTimezone"); 249782b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 249882b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden 2499d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtstart = values.getAsLong(Events.DTSTART) != null; 2500d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2501d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2502d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2503d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2504d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasRrule || hasRdate) { 2505d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!validateRecurrenceRule(values)) { 2506d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2507d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.getAsString(Events.RRULE)); 2508d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2509d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2510d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 2511d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDtstart) { 2512bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2513d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTSTART cannot be empty."); 2514d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2515d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDuration && !hasDtend) { 2516bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2517d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 2518d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "an event."); 2519d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2520d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasDuration && hasDtend) { 2521bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2522d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Cannot have both DTEND and DURATION in an event"); 2523d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2524d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2525d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 25269ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private void setEventDirty(long eventId) { 25279ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Object[] {eventId}); 25287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 25297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 253034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private long getOriginalId(String originalSyncId) { 253134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (TextUtils.isEmpty(originalSyncId)) { 253234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return -1; 253334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 253434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 253534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = -1; 253634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 253734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 253834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, 253934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._SYNC_ID + "=?", new String[] {originalSyncId}, null); 254034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 254134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalId = c.getLong(0); 254234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 254334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 254434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 254534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 254634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 254734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 254834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalId; 254934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 255034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 255134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private String getOriginalSyncId(long originalId) { 255234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId == -1) { 255334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return null; 255434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 255534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 255634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = null; 255734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 255834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 255934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, new String[] {Events._SYNC_ID}, 256034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._ID + "=?", new String[] {Long.toString(originalId)}, null); 256134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 256234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalSyncId = c.getString(0); 256334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 256434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 256534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 256634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 256734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 256834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 256934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalSyncId; 257034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 257134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 25724755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private Cursor getColorByTypeIndex(String accountName, String accountType, long colorType, 25734755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan String colorIndex) { 25744755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan return mDb.query(Tables.COLORS, COLORS_PROJECTION, COLOR_FULL_SELECTION, new String[] { 25754755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan accountName, accountType, Long.toString(colorType), colorIndex 25764755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan }, null, null, null); 25772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 25782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 25799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2580f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * Gets a calendar's "owner account", i.e. the e-mail address of the owner of the calendar. 2581f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * 2582f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * @param calId The calendar ID. 25839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 25849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2586f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2587f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2588f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2589f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2590f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2591f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 25949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 25959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 25969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 25979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 25989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 25999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 26009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 26019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2602f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2603f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2604f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 26059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 26069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 26089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 26099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 26109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 26119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 26149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private Account getAccount(long calId) { 26172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = null; 26182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor cursor = null; 26192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 26202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 26212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ACCOUNT_PROJECTION, null /* selection */, null /* selectionArgs */, 26222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* sort */); 26232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor == null || !cursor.moveToFirst()) { 26242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (Log.isLoggable(TAG, Log.DEBUG)) { 26252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 26262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return null; 26282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik account = new Account(cursor.getString(ACCOUNT_NAME_INDEX), 26302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.getString(ACCOUNT_TYPE_INDEX)); 26312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 26322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) { 26332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 26342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return account; 26372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 26469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 26479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 26499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 26509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 26519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 26529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 26539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 26549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 26559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 26569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 26579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 26589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 26609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 26619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 26629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 26659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 26669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 26679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 26689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 266924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param attendeeValues the column values for one row in the Attendees table. 26709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 26719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 26729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 267324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Long eventIdObj = attendeeValues.getAsLong(Attendees.EVENT_ID); 267424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (eventIdObj == null) { 267524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.w(TAG, "Attendee update values don't include an event_id"); 267624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return; 267724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 267824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = eventIdObj; 26799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 26819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 26829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 26839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 26849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 26869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 26879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 26899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 26909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2691f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2692f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2693f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 27099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 27119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 27129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2713f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2714f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2715f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 27319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 27339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 27369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 27379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 274324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Select a default value for "status" based on the relationship. 27449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 274524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer relationObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 274624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (relationObj != null) { 274724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int rel = relationObj; 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 27499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 27509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 275324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // If the status is specified, use that. 275424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer statusObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 275524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (statusObj != null) { 275624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden status = statusObj; 27579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 27609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2761b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2762b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2765d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 27661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Set the "hasAlarm" column in the database. 27671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 27681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param eventId The _id of the Event to update. 27691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param val The value to set it to (0 or 1). 27701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 27711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private void setHasAlarm(long eventId, int val) { 27721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues values = new ContentValues(); 27731c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden values.put(Events.HAS_ALARM, val); 27741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int count = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 27751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 27761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (count != 1) { 27771c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Log.w(TAG, "setHasAlarm on event " + eventId + " updated " + count + 27781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden " rows (expected 1)"); 27791c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 27801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 27811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 27821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 2783d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Calculates the "last date" of the event. For a regular event this is the start time 2784d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * plus the duration. For a recurring event this is the start date of the last event in 2785d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence, plus the duration. The event recurs forever, this returns -1. If 2786d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence rule can't be parsed, this returns -1. 2787d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2788d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values 2789d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the date, in milliseconds, since the start of the epoch (UTC), or -1 if an 2790d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * exceptional condition exists. 2791d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws DateException 2792d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 27939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 27949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 27959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 27969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 27979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 27989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 27999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 28009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 28019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 28029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 28039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 28049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 28059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 28079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 28099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 28109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 28129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 28139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 28149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 28159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 28169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 28179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 28189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 28199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 28209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 28219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 28229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 28249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 28259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2827f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2828f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2829f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2830f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2831f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2832f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2833b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik values.get(CalendarContract.Events.RRULE), e); 2834f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2835d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: this should throw an exception or return a distinct error code 2836f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2837f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2839f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 28409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 28419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 28429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 28449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 28489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 28509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 28529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 28549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 28559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 2856d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // repeats forever 28579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 28589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 28609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 28619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 28629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 28659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 28669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 28689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2870e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2871e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 287282b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * @param values the ContentValues (in/out); must include DTSTART and, if the event is 287382b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * recurring, the columns necessary to process a recurrence rule (RRULE, DURATION, 287482b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * EVENT_TIMEZONE, etc). 2875e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2876e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2877e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 28789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 28799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 28809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 28819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 28829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 28859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 28869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2887f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2888f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2889f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 28909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 28919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2894d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2895d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Creates or updates an entry in the EventsRawTimes table. 2896d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2897d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param eventId The ID of the event that was just created or is being updated. 2898d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values For a new event, the full set of event values; for an updated event, 2899d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the set of values that are being changed. 2900d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 29039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2904b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.EVENT_ID, eventId); 29059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 29079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 29099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 29109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 29119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 29129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 29159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 29169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 29179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 29209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 29219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 29229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 29239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 2924b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTSTART_2445, time.format2445()); 29259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 29289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 29299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 2930b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTEND_2445, time.format2445()); 29319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 29349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 29359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 29369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 29379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 29389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 29399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 29409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 29419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 2943b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.ORIGINAL_INSTANCE_TIME_2445, 2944b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 29459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 29489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 29499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 29509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 2951b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.LAST_DATE_2445, time.format2445()); 29529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 29559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2958b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2959b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2960ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 29619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 29629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 29640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_DELETE, uri, null, callerIsSyncAdapter, match, 29650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 29660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 29679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 29689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 29709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 29722ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 29739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 29749323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2975dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2976dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2977dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2978dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 29799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 29812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return deleteMatchingColors(appendAccountToSelection(uri, selection), 29822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 29832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 29841ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 29859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 29867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 29870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection = appendSyncAccountToSelection(uri, selection); 29887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 29891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 2990ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION, 29911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 29927e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 29939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 29941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 29951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 299610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 29979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2998420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 2999dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 30009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 30019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 30029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 30039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 30059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30061ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 30071ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 30081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 300910b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 30101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 3011bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID2: 3012bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden { 3013bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // This will throw NumberFormatException on missing or malformed input. 3014bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden List<String> segments = uri.getPathSegments(); 3015bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long eventId = Long.parseLong(segments.get(1)); 3016bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long excepId = Long.parseLong(segments.get(2)); 3017bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that this is an exception instance (has an ORIGINAL_ID field 3018bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // that matches the supplied eventId) 3019bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return deleteEventInternal(excepId, callerIsSyncAdapter, false /* isBatch */); 3020bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 30219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 30229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3024b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 30257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 30261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, selection, 30271c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 30287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 30319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 30337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3034b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 3035b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 30367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 30371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, null /* selection */, 30382fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 30397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 30429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, false, selection, selectionArgs, callerIsSyncAdapter); 30449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 30469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, true, null /*selection*/, null /*selectionArgs*/, 30481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden callerIsSyncAdapter); 30492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 30512fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 30522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 3053b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 30542fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 30551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, selection, 3056b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 30572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 30592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 30602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 30612fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 30622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 3063b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 3064636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 30652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 30661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, 30671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null /* selection */, null /* selectionArgs */); 30687e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3073b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 30747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 30751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.CALENDAR_ALERTS, uri, selection, 30761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 30777e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 30809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 30812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 30822fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 30839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3084b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 3085b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 30869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 30882ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik StringBuilder selectionSb = new StringBuilder(Calendars._ID + "="); 30899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 30909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 30919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 30929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 30939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 30949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 309624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // $FALL-THROUGH$ - fall through to CALENDARS for the actual delete 30979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 3098595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 309974ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 31009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 31019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 31026db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 3103315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 31049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 31059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 31069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 31079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 311010b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 31111ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 3112192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 31131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 31141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 3115b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 3116b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 3117636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 31181ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 31191ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 31201ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 31211ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 31221ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 312348f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 31241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 31251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 31261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 31271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 31281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 31291ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 3130b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origId = cursor.getString(EVENTS_ORIGINAL_ID_INDEX); 3131b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origSyncId = cursor.getString(EVENTS_ORIGINAL_SYNC_ID_INDEX); 3132b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (isRecurrenceEvent(rrule, rdate, origId, origSyncId)) { 31331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 31341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31354d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden boolean isRecurrence = !TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate); 31361ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 313748f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 313848f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 3139bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // 3140bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The EVENTS_CLEANUP_TRIGGER_SQL trigger will remove all associated data 3141bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Attendees, Instances, Reminders, etc). 314248f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 3143b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 31444d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 31454d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // If this is a recurrence, and the event was never synced with the server, 31464d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we want to delete any exceptions as well. (If it has been to the server, 31474d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we'll let the sync adapter delete the events explicitly.) We assume that, 31484d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // if the recurrence hasn't been synced, the exceptions haven't either. 31494d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden if (isRecurrence && emptySyncId) { 31504d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID, selectionArgs); 31514d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden } 31521ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 3153bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Event is on the server, so we "soft delete", i.e. mark as deleted so that 3154bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the sync adapter has a chance to tell the server about the deletion. After 3155bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the server sees the change, the sync adapter will do the "hard delete" 3156bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (above). 31571ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 31581b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 3159c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 3160b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 316102494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 31624d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // Exceptions that have been synced shouldn't be deleted -- the sync 31634d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // adapter will take care of that -- but we want to "soft delete" them so 31644d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // that they will be removed from the instances list. 31654d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // TODO: this seems to confuse the sync adapter, and leaves you with an 31664d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // invisible "ghost" event after the server sync. Maybe we can fix 31674d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // this by making instance generation smarter? Not vital, since the 31684d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exception instances disappear after the server sync. 31694d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden //mDb.update(Tables.EVENTS, values, SQL_WHERE_ORIGINAL_ID_HAS_SYNC_ID, 31704d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // selectionArgs); 31714d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 31724d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // It's possible for the original event to be on the server but have 31734d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exceptions that aren't. We want to remove all events with a matching 31744d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // original_id and an empty _sync_id. 31754d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID, 31764d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden selectionArgs); 31774d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 317843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 317943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 3180b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 3181b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 3182b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 3183b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 3184b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 3185b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 31861ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31881ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 31891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 31901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 31911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31928f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 319310b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 3194420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3195dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 319610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 31971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 31981ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 31991ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 32007e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 32011c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Delete rows from an Event-related table (e.g. Attendees) and mark corresponding events 32021c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * as dirty. 32031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 32057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 32067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 32077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 32087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 32091c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteFromEventRelatedTable(String table, Uri uri, String selection, 32101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden String[] selectionArgs) { 32111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (table.equals(Tables.EVENTS)) { 32121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("Don't delete Events with this method " 32131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "(use deleteEventInternal)"); 32141c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32151c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32161c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 32171c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 32181c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32191c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32201c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Re-issue the delete URI as a query. Note that, if this is a by-ID request, the ID 32211c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * will be in the URI, not selection/selectionArgs. 32221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32231c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Note that the query will return data according to the access restrictions, 32241c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * so we don't need to worry about deleting data we don't have permission to read. 32251c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, GENERIC_EVENT_ID); 32277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 32287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 32291c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long prevEventId = -1; 32301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 32311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long id = c.getLong(ID_INDEX); 32321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = c.getLong(EVENT_ID_INDEX); 32331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Duplicate the event. As a minor optimization, don't try to duplicate an 32341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // event that we just duplicated on the previous iteration. 32351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 32361c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 32371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden prevEventId = eventId; 32381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32399ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.delete(table, SQL_WHERE_ID, new String[]{String.valueOf(id)}); 32401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 32411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 32421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId)} ); 32431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 32457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 32477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 32487e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 32507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 32517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 32527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 32531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Deletes rows from the Reminders table and marks the corresponding events as dirty. 32541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Ensures the hasAlarm column in the Event is updated. 32551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @return The number of rows deleted. 32571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteReminders(Uri uri, boolean byId, String selection, String[] selectionArgs, 32591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden boolean callerIsSyncAdapter) { 32601c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this is a by-ID URI, make sure we have a good ID. Also, confirm that the 32621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * selection is null, since we will be ignoring it. 32631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long rowId = -1; 32651c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 32661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!TextUtils.isEmpty(selection)) { 32671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 32681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden rowId = ContentUris.parseId(uri); 32701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (rowId < 0) { 32711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 32721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32731c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Determine the set of events affected by this operation. There can be multiple 32771c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders with the same event_id, so to avoid beating up the database with "how many 32781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders are left" and "duplicate this event" requests, we want to generate a list 32791c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * of affected event IDs and work off that. 32801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 32811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * TODO: use GROUP BY to reduce the number of rows returned in the cursor. (The content 32821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * provider query() doesn't take it as an argument.) 32831c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32841c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden HashSet<Long> eventIdSet = new HashSet<Long>(); 32851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, new String[] { Attendees.EVENT_ID }, selection, selectionArgs, null); 32861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden try { 32871c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 32881c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden eventIdSet.add(c.getLong(0)); 32891c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32901c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } finally { 32911c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden c.close(); 32921c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 32931c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 32941c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 32951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this isn't a sync adapter, duplicate each event (along with its associated tables), 32961c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * and mark each as "dirty". This is for the benefit of partial-update sync. 32971c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 32981c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!callerIsSyncAdapter) { 32991c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 33001c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 33011c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33021c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 33031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 33041c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 33051c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 33061c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 33071c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 33081c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33091c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Issue the original deletion request. If we were called with a by-ID URI, generate 33131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * a selection. 33141c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33151c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 33161c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selection = SQL_WHERE_ID; 33171c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 33181c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33191c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int delCount = mDb.delete(Tables.REMINDERS, selection, selectionArgs); 33201c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33211c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * For each event, set "hasAlarm" to zero if we've deleted the last of the reminders. 33231c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * (If the event still has reminders, hasAlarm should already be 1.) Because we're 33241c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * executing in an exclusive transaction there's no risk of racing against other 33251c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * database updates. 33261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33271c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues noAlarmValues = new ContentValues(); 33281c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden noAlarmValues.put(Events.HAS_ALARM, 0); 33291c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 33301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 33311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 33321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Count up the number of reminders still associated with this event. 33341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor reminders = mDb.query(Tables.REMINDERS, new String[] { GENERIC_ID }, 33351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden SQL_WHERE_EVENT_ID, new String[] { String.valueOf(eventId) }, 33361c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null, null, null); 33371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int reminderCount = reminders.getCount(); 33381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden reminders.close(); 33391c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (reminderCount == 0) { 33411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, noAlarmValues, SQL_WHERE_ID, 33421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 33431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33441c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return delCount; 33471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33491c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 335024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Update rows in a table and, if this is a non-sync-adapter update, mark the corresponding 335124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * events as dirty. 335224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * <p> 335324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * This only works for tables that are associated with an event. It is assumed that the 335424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * link to the Event row is a numeric identifier in a column called "event_id". 335524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 335624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param uri The original request URI. 335724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param byId Set to true if the URI is expected to include an ID. 335824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param updateValues The new values to apply. Not all columns need be represented. 335924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selection For non-by-ID operations, the "where" clause to use. 336024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selectionArgs For non-by-ID operations, arguments to apply to the "where" clause. 336124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param callerIsSyncAdapter Set to true if the caller is a sync adapter. 336224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @return The number of rows updated. 33637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 336424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private int updateEventRelatedTable(Uri uri, String table, boolean byId, 336524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues updateValues, String selection, String[] selectionArgs, 336624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden boolean callerIsSyncAdapter) 336724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden { 336824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 336924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Confirm that the request has either an ID or a selection, but not both. It's not 337024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * actually "wrong" to have both, but it's not useful, and having neither is likely 337124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * a mistake. 337224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 337324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * If they provided an ID in the URI, convert it to an ID selection. 337424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 337524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (byId) { 337624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!TextUtils.isEmpty(selection)) { 337724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 337824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 337924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long rowId = ContentUris.parseId(uri); 338024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (rowId < 0) { 338124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 338224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 338324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selection = SQL_WHERE_ID; 338424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 338524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } else { 338624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (TextUtils.isEmpty(selection)) { 338724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection is required for " + uri); 338824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 338924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 339024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 339124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 339224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Query the events to update. We want all the columns from the table, so we us a 339324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * null projection. 339424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 339524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Cursor c = mDb.query(table, null /*projection*/, selection, selectionArgs, 339624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, null); 33977e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 33987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 339924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (c.getCount() == 0) { 340024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.d(TAG, "No query results for " + uri + ", selection=" + selection + 340124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 340224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return 0; 340324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 340424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 340524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues dirtyValues = null; 340624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 340724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues = new ContentValues(); 340824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues.put(Events.DIRTY, "1"); 340924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 341024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 341124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int idIndex = c.getColumnIndex(GENERIC_ID); 341224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int eventIdIndex = c.getColumnIndex(GENERIC_EVENT_ID); 341324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (idIndex < 0 || eventIdIndex < 0) { 341424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new RuntimeException("Lookup on _id/event_id failed for " + uri); 341524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 341624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 341724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 341824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * For each row found: 341924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - merge original values with update values 342024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update database 342124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - if not sync adapter, set "dirty" flag in corresponding event to 1 342224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update Event attendee status 342324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 342424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden while (c.moveToNext()) { 342524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* copy the original values into a ContentValues, then merge the changes in */ 342624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues values = new ContentValues(); 342724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden DatabaseUtils.cursorRowToContentValues(c, values); 342824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden values.putAll(updateValues); 342924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 343024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long id = c.getLong(idIndex); 343124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = c.getLong(eventIdIndex); 343224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 343324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Make a copy of the original, so partial-update code can see diff. 343424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDbHelper.duplicateEvent(eventId); 343524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 343624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(table, values, SQL_WHERE_ID, new String[] { String.valueOf(id) }); 343724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 343824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 343924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden new String[] { String.valueOf(eventId) }); 344024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 34417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 344224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 344324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 344424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * The Events table has a "selfAttendeeStatus" field that usually mirrors the 344524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * "attendeeStatus" column of one row in the Attendees table. It's the provider's 344624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * job to keep these in sync, so we have to check for changes here. (We have 344724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * to do it way down here because this is the only point where we have the 344824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * merged Attendees values.) 344924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 345024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * It's possible, but not expected, to have multiple Attendees entries with 345124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * matching attendeeEmail. The behavior in this case is not defined. 345224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 345324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * We could do this more efficiently for "bulk" updates by caching the Calendar 345424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * owner email and checking it here. 345524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 345624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (table.equals(Tables.ATTENDEES)) { 345724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden updateEventAttendeeStatus(mDb, values); 345824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 34597e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 34617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 34627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 34647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 34662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int deleteMatchingColors(String selection, String[] selectionArgs) { 34672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // query to find all the colors that match, for each 34682f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - verify no one references it 34692f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - delete color 34702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, null, 34712f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null, null); 34722f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c == null) { 34732f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return 0; 34742f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 34752f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 34762f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c2 = null; 34772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 34782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String index = c.getString(COLORS_COLOR_INDEX_INDEX); 34792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = c.getString(COLORS_ACCOUNT_NAME_INDEX); 34802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = c.getString(COLORS_ACCOUNT_TYPE_INDEX); 34812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean isCalendarColor = c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 34822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 34832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (isCalendarColor) { 34842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = mDb.query(Tables.CALENDARS, ID_ONLY_PROJECTION, 34852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik SQL_WHERE_CALENDAR_COLOR, new String[] { 34862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName, accountType, index 34872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }, null, null, null); 34882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 34892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 34902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " calendars."); 34912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 34922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 34932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 34942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, SQL_WHERE_EVENT_COLOR, 34952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] {accountName, accountType, index}, null); 34962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 34972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 34982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " events."); 34992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 35002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 35032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2 != null) { 35042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2.close(); 35052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 35092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 35102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 35112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return mDb.delete(Tables.COLORS, selection, selectionArgs); 35142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 35152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 351674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 35179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 35189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 35199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 352074ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 352174ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 352274ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 352374ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 352474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 35259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 35269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 35279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 35299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 35309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 35319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 35329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 35349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 35359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 353674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 35379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3539fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 3540fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 3541fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3542fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 3543fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3544fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3545fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3546fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 3547fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 3548fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 3549fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3550fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3551fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 3552fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 3553fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3554fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 3555fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 3556fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 3557fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3558fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 3559fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 3560fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 3561d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean doesStatusCancelUpdateMeanUpdate(ContentValues values, 3562d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues modValues) { 3563d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isStatusCanceled = modValues.containsKey(Events.STATUS) && 3564d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden (modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 3565fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 3566d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String originalSyncId = values.getAsString(Events.ORIGINAL_SYNC_ID); 3567d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3568d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!TextUtils.isEmpty(originalSyncId)) { 3569d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This event is an exception. See if the recurring event still exists. 3570d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return doesEventExistForSyncId(originalSyncId); 3571d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3572d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3573d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This is the normal case, we just want an UPDATE 3574d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return true; 3575d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3576d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 35772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int handleUpdateColors(ContentValues values, String selection, String[] selectionArgs) { 35782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 35792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int result = mDb.update(Tables.COLORS, values, selection, selectionArgs); 35802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (values.containsKey(Colors.COLOR)) { 35812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 35822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, 35832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* groupBy */, null /* having */, null /* orderBy */); 35842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 35852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean calendarColor = 35862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 35872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = c.getInt(COLORS_COLOR_INDEX); 35882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String[] args = { 35892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_NAME_INDEX), 35902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_TYPE_INDEX), 35912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_COLOR_INDEX_INDEX) 35922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 35932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ContentValues colorValue = new ContentValues(); 35942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendarColor) { 35952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Calendars.CALENDAR_COLOR, color); 3596d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.CALENDARS, colorValue, SQL_WHERE_CALENDAR_COLOR, args); 35972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 35982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Events.EVENT_COLOR, color); 3599d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.EVENTS, colorValue, SQL_WHERE_EVENT_COLOR, args); 36002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 36032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 36042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 36052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return result; 36092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 3611d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3612d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 3613d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Handles a request to update one or more events. 3614d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 3615d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * The original event(s) will be loaded from the database, merged with the new values, 3616d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * and the result checked for validity. In some cases this will alter the supplied 3617d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * arguments (e.g. zeroing out the times on all-day events), change additional fields (e.g. 3618d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * update LAST_DATE when DTSTART changes), or cause modifications to other tables (e.g. reset 3619d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Instances when a recurrence rule changes). 3620d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3621d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param cursor The set of events to update. 36224b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden * @param updateValues The changes to apply to each event. 3623d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param callerIsSyncAdapter Indicates if the request comes from the sync adapter. 3624d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the number of rows updated 3625d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 36264b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden private int handleUpdateEvents(Cursor cursor, ContentValues updateValues, 3627d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean callerIsSyncAdapter) { 3628d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* 36291c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * This field is considered read-only. It should not be modified by applications or 36301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * by the sync adapter. 36311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 36321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updateValues.remove(Events.HAS_ALARM); 36331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 36341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 3635d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For a single event, we can just load the event, merge modValues in, perform any 3636d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * fix-ups (putting changes into modValues), check validity, and then update(). We have 3637d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * to be careful that our fix-ups don't confuse the sync adapter. 3638d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3639d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For multiple events, we need to load, merge, and validate each event individually. 3640d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * If no single-event-specific changes need to be made, we could just issue the original 3641d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * bulk update, which would be more efficient than a series of individual updates. 3642d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * However, doing so would prevent us from taking advantage of the partial-update 3643d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * mechanism. 3644d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 3645d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (cursor.getCount() > 1) { 3646d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3647d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "Performing update on " + cursor.getCount() + " events"); 3648d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3649d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3650d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden while (cursor.moveToNext()) { 36519f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Make a copy of updateValues so we can make some local changes. 36524b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden ContentValues modValues = new ContentValues(updateValues); 36539f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 36549f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Load the event into a ContentValues object. 3655d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues values = new ContentValues(); 3656d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 36579f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden boolean doValidate = false; 36589f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (!callerIsSyncAdapter) { 36599f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden try { 36609f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Check to see if the data in the database is valid. If not, we will skip 36619f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // validation of the update, so that we don't blow up on attempts to 36629f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // modify existing badly-formed events. 36639f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden validateEventData(values); 36649f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden doValidate = true; 36659f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } catch (IllegalArgumentException iae) { 36669f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden Log.d(TAG, "Event " + values.getAsString(Events._ID) + 36679f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden " malformed, not validating update (" + 36689f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden iae.getMessage() + ")"); 36699f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 36709f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 36719f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 36729f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Merge the modifications in. 3673d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.putAll(modValues); 3674d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 36752f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If a color_index is being set make sure it's valid 3676387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = modValues.getAsString(Events.EVENT_COLOR_KEY); 36772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 36782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 36792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 36802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.CALENDARS, ACCOUNT_PROJECTION, SQL_WHERE_ID, 36812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] { values.getAsString(Events.CALENDAR_ID) }, null, null, null); 36822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 36832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.moveToFirst()) { 36842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = c.getString(ACCOUNT_NAME_INDEX); 36852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = c.getString(ACCOUNT_TYPE_INDEX); 36862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 36882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 36892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 36902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_EVENT); 36932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 36959f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Scrub and/or validate the combined event. 3696be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 3697be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, modValues); 36989f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 36999f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (doValidate) { 3700be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 3701be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 3702d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3703d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Look for any updates that could affect LAST_DATE. It's defined as the end of 3704d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // the last meeting, so we need to pay attention to DURATION. 3705d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3706d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DTEND) || 3707d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DURATION) || 3708d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EVENT_TIMEZONE) || 3709d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RRULE) || 3710d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RDATE) || 3711d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXRULE) || 3712d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXDATE)) { 3713d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long newLastDate; 3714d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3715d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden newLastDate = calculateLastDate(values); 3716d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } catch (DateException de) { 3717d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Unable to compute LAST_DATE", de); 3718d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3719d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long oldLastDateObj = values.getAsLong(Events.LAST_DATE); 3720d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long oldLastDate = (oldLastDateObj == null) ? -1 : oldLastDateObj; 3721d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (oldLastDate != newLastDate) { 3722d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This overwrites any caller-supplied LAST_DATE. This is okay, because the 3723d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // caller isn't supposed to be messing with the LAST_DATE field. 3724d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (newLastDate < 0) { 3725d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.putNull(Events.LAST_DATE); 3726d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3727d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.LAST_DATE, newLastDate); 3728fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3729fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3730d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3731d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3732d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3733d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DIRTY, 1); 3734d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3735fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3736d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Disallow updating the attendee status in the Events 3737d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // table. In the future, we could support this but we 3738d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // would have to query and update the attendees table 3739d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to keep the values consistent. 3740d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 3741d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Updating " 3742d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + Events.SELF_ATTENDEE_STATUS 3743d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + " in Events table is not allowed."); 3744d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3745d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3746d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(values, modValues)) { 3747d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.WARN)) { 3748d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.w(TAG, "handleUpdateEvents: " + 3749d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "allDay is true but sec, min, hour were not 0."); 3750fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3751d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3752d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3753d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // For taking care about recurrences exceptions cancelations, check if this needs 3754d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to be an UPDATE or a DELETE 3755d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isUpdate = doesStatusCancelUpdateMeanUpdate(values, modValues); 3756d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3757d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = values.getAsLong(Events._ID); 3758d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3759d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (isUpdate) { 3760d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If a user made a change, possibly duplicate the event so we can do a partial 3761d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // update. If a sync adapter made a change and that change marks an event as 3762d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // un-dirty, remove any duplicates that may have been created earlier. 3763d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3764d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.duplicateEvent(id); 3765d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3766d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DIRTY) 3767d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden && modValues.getAsInteger(Events.DIRTY) == 0) { 3768d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.removeDuplicateEvent(id); 3769d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3770d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3771d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int result = mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 3772d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden new String[] { String.valueOf(id) }); 3773d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (result > 0) { 3774d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden updateEventRawTimesLocked(id, modValues); 3775d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mInstancesHelper.updateInstancesLocked(modValues, id, 3776d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden false /* not a new event */, mDb); 3777d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3778d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // XXX: should we also be doing this when RRULE changes (e.g. instances 3779d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // are introduced or removed?) 3780d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3781d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.STATUS)) { 3782d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If this is a cancellation knock it out 3783d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // of the instances table 3784d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.STATUS) && 3785d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 3786d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String[] args = new String[] {String.valueOf(id)}; 3787d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 3788d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3789d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3790d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // The start time or status of the event changed, so run the 3791d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // event alarm scheduler. 3792d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3793d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "updateInternal() changing event"); 3794d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3795d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3796d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3797d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3798d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(id, callerIsSyncAdapter); 3799d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3800d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3801d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3802d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3803d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(callerIsSyncAdapter); 3804fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3805fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3806d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3807d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return cursor.getCount(); 3808fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3809fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 38109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 38119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 3812b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 3813ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 38149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 38159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 38170739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_UPDATE, uri, values, callerIsSyncAdapter, match, 38180739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 38199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 38219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 38229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 38239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 38249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 38269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 38272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 3828dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 38299323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3830dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3831dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3832dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 38339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 38362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Integer color = values.getAsInteger(Colors.COLOR); 38372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (values.size() > 1 || (values.size() == 1 && color == null)) { 38382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("You may only change the COLOR " 38392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + "for an existing Colors entry."); 38402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return handleUpdateColors(values, appendAccountToSelection(uri, selection), 38422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 38432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 384443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 38459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 38469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 384743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 384843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 384943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 385043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 385143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 385243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 385343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 385443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 385543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 38564cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik if (selection != null && TextUtils.equals(selection,"_id=?")) { 38574cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik id = Long.parseLong(selectionArgs[0]); 38584cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik } else if (selection != null && selection.startsWith("_id=")) { 385943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 386043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 386143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 386243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 3863b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 386443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 386543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 386643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 3867c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Calendars.DIRTY, 1); 38682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 38699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 38709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 38719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 38729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3873387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 38742f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 38752f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 38762f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 38772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 38782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(id); 38792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 38802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 38812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 38822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_CALENDAR); 38852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3887b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 3888636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 38899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38903ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3891d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 38924067700dbedcf4c8a379c9ecba9b5603972b4607Andy McFadden if (values.containsKey(Calendars.VISIBLE)) { 3893d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3894d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3895d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3896d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3897420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 3898d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 38993ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3900dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 39013ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 39023ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 39039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 39049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39057e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 39069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 39079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 3908d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Cursor events = null; 39099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3910d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Grab the full set of columns for each selected event. 3911d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: define a projection with just the data we need (e.g. we don't need to 3912d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // validate the SYNC_* columns) 39139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3914d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3915d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (match == EVENTS_ID) { 3916d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Single event, identified by ID. 3917d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = ContentUris.parseId(uri); 3918d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3919d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden SQL_WHERE_ID, new String[] { String.valueOf(id) }, 3920d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 39219ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } else { 3922d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // One or more events, identified by the selection / selectionArgs. 3923d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3924d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden selection, selectionArgs, 3925d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 39269ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 392706c305d35741db303bd3aacd0eab5af8de0ab34eErik 3928d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events.getCount() == 0) { 392924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.i(TAG, "No events to update: uri=" + uri + " selection=" + selection + 3930d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 3931d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return 0; 3932d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 39333ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3934d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return handleUpdateEvents(events, values, callerIsSyncAdapter); 3935d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } finally { 3936d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events != null) { 3937d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events.close(); 3938fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 39399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 394124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES: 394224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, false, values, selection, 394324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 394424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES_ID: 394524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, true, values, null, null, 394624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 39479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 39492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 39502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 39519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3952b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 3953636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 39549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 39562fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 39572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 3958b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 39599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 396024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 396124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case REMINDERS: 396224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.REMINDERS, false, values, selection, 396324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 39642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 396524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int count = updateEventRelatedTable(uri, Tables.REMINDERS, true, values, null, null, 396624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 39677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 39689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 39699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 39709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 39719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 39729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3973420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 39747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 39759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 397624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 397724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case EXTENDED_PROPERTIES_ID: 397824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.EXTENDED_PROPERTIES, true, values, 397924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, callerIsSyncAdapter); 398024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 398183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 398283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 398383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 3984420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 398583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 398683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 398783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 3988420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(true); 398983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 399083512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 39919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3992315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 3993315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 3994315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 3995315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3996315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3997315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 3998315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3999315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 4000315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 4001315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 4002315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4003315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4004315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 4005315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 4006315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4007315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 4008315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 4009b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 4010315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4011315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 4012f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "home", set the Instances 4013f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the previous 4014f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "auto", set the Instances 4015f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the current 4016f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // device one 4017f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone Instances is set AND if we are in "home" 4018f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone type, then save the timezone Instance into 4019f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // "previous" too 4020315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 4021315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 4022315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 4023315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 4024315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 4025315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 4026315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 4027315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 4028315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 4029315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 4030315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 4031315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4032315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 4033d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 4034315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 4035315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4036d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4037315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4038315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4039315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 4040315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 4041315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 4042315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 4043315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 4044315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4045d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4046315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4047315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4048315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4049315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4050315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 4051315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 4052315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 4053315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 4054315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 4055315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 4056315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 4057315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 4058d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 4059315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 4060315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 4061315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4062d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4063315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4064315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4065315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4066315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4067315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 4068315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4069315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 40709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 40719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 40729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40752f251c778c06d21ed7693a70f4a1268ff929242eRoboErik /** 40762f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * Verifies that a color with the given index exists for the given Calendar 40772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * entry. 40782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * 40792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountName The email of the account the color is for 40802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountType The type of account the color is for 40814755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorIndex The color_index being set for the calendar 40824755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorType The type of color expected (Calendar/Event) 40832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @return The color specified by the index 40842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik */ 40854755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private int verifyColorExists(String accountName, String accountType, String colorIndex, 40864755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan int colorType) { 40872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 40882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Cannot set color. A valid account does" 40892f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " not exist for this calendar."); 40902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 40912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color; 40922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 40932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 40944755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 40954755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan if (!c.moveToFirst()) { 40964755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("Color type: " + colorType + " and index " 40974755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + colorIndex + " does not exist for account."); 40982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 40992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik color = c.getInt(COLORS_COLOR_INDEX); 41002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 41012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 41022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 41032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return color; 41062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 41072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 41089ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendAccountFromParameterToSelection(String selection, Uri uri) { 4109b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 4110b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 4111b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 4112b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 4113595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 41149ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 41159ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(Calendars.ACCOUNT_NAME + "=") 41169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountName)) 41179ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" AND ") 41189ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(Calendars.ACCOUNT_TYPE) 41199ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" = ") 41209ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountType)); 41219ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 4122595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 41239ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 41249ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41259ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41269ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 41279ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendLastSyncedColumnToSelection(String selection, Uri uri) { 41289ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (getIsCallerSyncAdapter(uri)) { 41299ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 4130595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 41319ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 4132b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sb.append(CalendarContract.Events.LAST_SYNCED).append(" = 0"); 41339ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 4134595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 4135595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 41369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 4137b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 4138b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 4139b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 4140b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 41419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 4142b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Calendars.ACCOUNT_NAME 4143b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + "=" + DatabaseUtils.sqlEscapeString(accountName) + " AND " 4144b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Calendars.ACCOUNT_TYPE + "=" 41450739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountType)); 41469ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 41470739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 41480739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return selection; 41490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 41500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 41510739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 41520739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private String appendSyncAccountToSelection(Uri uri, String selection) { 41530739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountName = QueryParameterUtils.getQueryParameter(uri, 4154b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 41550739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountType = QueryParameterUtils.getQueryParameter(uri, 4156b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 41570739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (!TextUtils.isEmpty(accountName)) { 4158b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Events.ACCOUNT_NAME + "=" 41590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountName) + " AND " 4160b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events.ACCOUNT_TYPE + "=" 41619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 41629ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 41639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 41649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 41659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41689ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendSelection(StringBuilder sb, String selection) { 41699ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!TextUtils.isEmpty(selection)) { 41709ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(" AND ("); 41719ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(selection); 41729ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(')'); 41739ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41749ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return sb.toString(); 41759ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 41769ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 41770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik /** 41780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * Verifies that the operation is allowed and throws an exception if it 41790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * isn't. This defines the limits of a sync adapter call vs an app call. 4180683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * <p> 4181683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * Also rejects calls that have a selection but shouldn't, or that don't have a selection 4182683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * but should. 4183c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik * 41840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param type The type of call, {@link #TRANSACTION_QUERY}, 41850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_INSERT}, {@link #TRANSACTION_UPDATE}, or 41860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_DELETE} 41870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param uri 41880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param values 41890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param isSyncAdapter 41900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik */ 41910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyTransactionAllowed(int type, Uri uri, ContentValues values, 41920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik boolean isSyncAdapter, int uriMatch, String selection, String[] selectionArgs) { 4193f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Queries are never restricted to app- or sync-adapter-only, and we don't 4194f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // restrict the set of columns that may be accessed. 4195f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden if (type == TRANSACTION_QUERY) { 4196f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden return; 4197f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4198f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 4199683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (type == TRANSACTION_UPDATE || type == TRANSACTION_DELETE) { 42002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // TODO review this list, document in contract. 4201683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (!TextUtils.isEmpty(selection)) { 4202683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Only allow selections for the URIs that can reasonably use them. 42032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Whitelist of URIs allowed selections 4204683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4205683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case SYNCSTATE: 4206683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDARS: 4207683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4208683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4209683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDAR_ALERTS: 4210683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4211683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EXTENDED_PROPERTIES: 4212683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 42132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 4214683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4215683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4216683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection not permitted for " + uri); 4217683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4218683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } else { 4219683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Disallow empty selections for some URIs. 42202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Blacklist of URIs _not_ allowed empty selections 4221683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4222683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4223683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4224683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4225683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 4226683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection must be specified for " 4227683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden + uri); 4228683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4229683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4230683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4231683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4232683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4233683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden 4234f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Only the sync adapter can use these to make changes. 42352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!isSyncAdapter) { 42362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik switch (uriMatch) { 42372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE: 42382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE_ID: 42392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES: 42402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES_ID: 42412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 42422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Only sync adapters may write using " + uri); 42432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik default: 42442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 4245f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4246f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4247f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 42480739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (type) { 42490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_INSERT: 42500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42510739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException( 42520739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Inserting into instances not supported"); 42530739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4254c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4255c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 42560739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42570739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42580739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 42590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 42600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 42610739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 42620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 42640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_UPDATE: 42650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Updating instances not supported"); 42670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4268c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4269c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 42700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 42730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 42740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 42750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 42760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 42780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_DELETE: 42790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 42800739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Deleting instances not supported"); 42810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 42830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 42840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 42850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 42870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 42900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyHasAccount(Uri uri, String selection, String[] selectionArgs) { 4291c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = QueryParameterUtils.getQueryParameter(uri, Calendars.ACCOUNT_NAME); 42920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String accountType = QueryParameterUtils.getQueryParameter(uri, 4293c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 42940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 42950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (selection != null && selection.startsWith(ACCOUNT_SELECTION_PREFIX)) { 42960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountName = selectionArgs[0]; 42970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountType = selectionArgs[1]; 42980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 42990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 43010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException( 43020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Sync adapters must specify an account and account type: " + uri); 43030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 4306c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private void verifyColumns(ContentValues values, int uriMatch) { 4307c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 4308c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik return; 4309c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4310c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String[] columns; 4311c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik switch (uriMatch) { 4312c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4313c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4314c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4315c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4316c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = Events.PROVIDER_WRITABLE_COLUMNS; 4317c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4318c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik default: 4319c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = PROVIDER_WRITABLE_DEFAULT_COLUMNS; 4320c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4321c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4322c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 4323c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik for (int i = 0; i < columns.length; i++) { 4324c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values.containsKey(columns[i])) { 4325c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik throw new IllegalArgumentException("Only the provider may write to " + columns[i]); 4326c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4327c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4328c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4329c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 43300739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyNoSyncColumns(ContentValues values, int uriMatch) { 4331c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 43320739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43330739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43340739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String[] syncColumns; 43350739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (uriMatch) { 43360739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS: 43370739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS_ID: 43380739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES: 43390739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES_ID: 4340c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Calendars.SYNC_WRITABLE_COLUMNS; 4341c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4342c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4343c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4344c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4345c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4346c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Events.SYNC_WRITABLE_COLUMNS; 43470739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 43480739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik default: 43490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik syncColumns = SYNC_WRITABLE_DEFAULT_COLUMNS; 43500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 43510739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 43520739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43530739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik for (int i = 0; i < syncColumns.length; i++) { 43540739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (values.containsKey(syncColumns[i])) { 43550739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException("Only sync adapters may write to " 43560739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + syncColumns[i]); 43570739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43580739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 43619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 43629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 43639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 43649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 4365c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik new String[] {Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, 4366fa332ecedc0c340109811552407142f6e4f600b2RoboErik Calendars.CAL_SYNC1, Calendars.SYNC_EVENTS}, 43679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 43689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 43699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 43709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 43719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 43729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 43739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 4374ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 43759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 4376ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 4377ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 4378ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 4379ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 4380ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 4381ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 4382ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 43839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 43842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) 43852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 43869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 43879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 43889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 43899535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 43909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 4391f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 4392f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 4393f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 4394f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 43959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 43969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 43979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 43989535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 43999535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 44009535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 44019535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 44029535627bf6295cd94447beb83e1aac41f50c3600Erik } 44039535627bf6295cd94447beb83e1aac41f50c3600Erik 44049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 44059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 44069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 44079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 44099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 44109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 44119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 44129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4414a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4415a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4416a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4417a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 4418dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 44199ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4420a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4421dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 4422dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 4423dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 4424a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4425a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4426a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4427a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4428a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4429a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 4430a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 4431a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 4432a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 4433a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 4434a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 44359ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param eventId the ID of the event that changed, or -1 for no specific event 44369ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4437a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4438dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 4439dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 4440a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 4441a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 4442a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 4443a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 4444a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 4445dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 4446dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 4447dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 4448dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 4449dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 4450dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 4451dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 4452dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 4453dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 4454dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 4455dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 4456dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 4457dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 4458dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 4459dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 4460dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 4461dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 4462a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 4463dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 4464a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4465a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4466a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4467a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 4468a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 44699ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #sendUpdateNotification(boolean)} instead. 4470a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 44719ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @see #sendUpdateNotification(boolean) 4472a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4473a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 4474a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 4475b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.CONTENT_URI); 4476f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 4477f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 4478f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 4479e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 4480a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4481a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 44820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_QUERY = 0; 44830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_INSERT = 1; 44840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_UPDATE = 2; 44850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_DELETE = 3; 44860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 44870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:off 44880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final String[] SYNC_WRITABLE_DEFAULT_COLUMNS = new String[] { 4489b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars.DIRTY, 4490b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars._SYNC_ID 44910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik }; 4492c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String[] PROVIDER_WRITABLE_DEFAULT_COLUMNS = new String[] { 4493c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik }; 44940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:on 44950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 44969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 44979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 44989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 44992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS = 4; 45002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS_ID = 5; 45012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES = 6; 45022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES_ID = 7; 45032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS = 8; 45042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS_ID = 9; 45052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES = 10; 45062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES_ID = 11; 45072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS = 12; 45082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_ID = 13; 45092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_BY_INSTANCE = 14; 45102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_BY_DAY = 15; 45112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE = 16; 45122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE_ID = 17; 45132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES = 18; 45142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES_ID = 19; 45152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_DAYS = 20; 45162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM = 21; 45172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM_REMOVE = 22; 45182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int TIME = 23; 45192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES = 24; 45202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES_ID = 25; 45212ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH = 26; 45222ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH_BY_DAY = 27; 45232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int PROVIDER_PROPERTIES = 28; 4524bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID = 29; 4525bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID2 = 30; 45263b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static final int EMMA = 31; 45272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS = 32; 45289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 45309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 45312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final HashMap<String, String> sColorsProjectionMap; 4532f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final HashMap<String, String> sEventsProjectionMap; 453319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 45349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 45359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 45369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 4537315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 453839c65e5716e21e863d8de587d139dae85f99422fFred Quintana private static final HashMap<String, String> sCountProjectionMap; 45399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 4541b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/when/*/*", INSTANCES); 4542b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 4543b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 4544b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/searchbyday/*/*/*", 454581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 4546b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 4547b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events", EVENTS); 4548b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events/#", EVENTS_ID); 4549b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities", EVENT_ENTITIES); 4550b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 4551b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars", CALENDARS); 4552b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars/#", CALENDARS_ID); 4553b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 4554b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 4555b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees", ATTENDEES); 4556b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees/#", ATTENDEES_ID); 4557b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders", REMINDERS); 4558b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders/#", REMINDERS_ID); 4559b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 4560b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties/#", 4561b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik EXTENDED_PROPERTIES_ID); 4562b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 4563b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 4564b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/by_instance", 4565b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 4566b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate", SYNCSTATE); 4567b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 4568b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, CalendarAlarmManager.SCHEDULE_ALARM_PATH, 4569420b7fb569773ae573fbe90c3a9c522d4c368863Erik SCHEDULE_ALARM); 4570b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, 4571b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 4572b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time/#", TIME); 4573b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time", TIME); 4574b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "properties", PROVIDER_PROPERTIES); 4575b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID); 4576b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2); 45773b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden sUriMatcher.addURI(CalendarContract.AUTHORITY, "emma", EMMA); 45782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "colors", COLORS); 45799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 458039c65e5716e21e863d8de587d139dae85f99422fFred Quintana /** Contains just BaseColumns._COUNT */ 458139c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap = new HashMap<String, String>(); 458239c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)"); 458339c65e5716e21e863d8de587d139dae85f99422fFred Quintana 45842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap = new HashMap<String, String>(); 45852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors._ID, Colors._ID); 45862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.DATA, Colors.DATA); 45872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_NAME, Colors.ACCOUNT_NAME); 45882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_TYPE, Colors.ACCOUNT_TYPE); 4589387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sColorsProjectionMap.put(Colors.COLOR_KEY, Colors.COLOR_KEY); 45902f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR_TYPE, Colors.COLOR_TYPE); 45912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR, Colors.COLOR); 45922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 45939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 45949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 459502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME); 459602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_TYPE, Events.ACCOUNT_TYPE); 4597c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.TITLE, Events.TITLE); 4598c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4599c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4600c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.STATUS, Events.STATUS); 460102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4602387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR_KEY, Events.EVENT_COLOR_KEY); 4603c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4604c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART); 4605c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTEND, Events.DTEND); 4606c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4607c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4608c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DURATION, Events.DURATION); 4609c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4610c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4611c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4612c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4613c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, Events.HAS_EXTENDED_PROPERTIES); 4614c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RRULE, Events.RRULE); 4615c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RDATE, Events.RDATE); 4616c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXRULE, Events.EXRULE); 4617c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXDATE, Events.EXDATE); 4618c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 461934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4620c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, Events.ORIGINAL_INSTANCE_TIME); 4621c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4622c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4623c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4624c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4625c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, Events.GUESTS_CAN_INVITE_OTHERS); 4626c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4627c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4628c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4629c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DELETED, Events.DELETED); 463002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 46319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4632e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 46331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 46341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 46351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 46369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 4637c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 4638387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR_KEY); 463902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL); 4640c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 464102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 4642c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 464302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 464402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 46452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap 46462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik .put(Calendars.ALLOWED_ATTENDEE_TYPES, Calendars.ALLOWED_ATTENDEE_TYPES); 46472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap.put(Calendars.ALLOWED_AVAILABILITY, Calendars.ALLOWED_AVAILABILITY); 464802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 464902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND); 465002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 4651c339afc7df041ebfc5f4587f78cf38562aa23459Alon Albert sEventsProjectionMap.put(Events.DISPLAY_COLOR, Events.DISPLAY_COLOR); 46529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4653982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 4654e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 4655e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 4656982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4657e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4658982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 4659c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events._ID, Events._ID); 466002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 466102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 466202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 466302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 466402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 466502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 46669ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 466702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 466802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 466902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 467002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 467102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 467202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 467302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 467402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 467502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 467602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 467702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 467802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 467902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 4680c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY); 46819ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 46829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 468346f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 4684c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.TITLE, Events.TITLE); 4685c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4686c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4687c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.STATUS, Events.STATUS); 468802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4689c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4690c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTSTART, Events.DTSTART); 4691c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTEND, Events.DTEND); 4692c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4693c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4694c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DURATION, Events.DURATION); 4695c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4696c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4697c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4698c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4699c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, 4700c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.HAS_EXTENDED_PROPERTIES); 4701c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RRULE, Events.RRULE); 4702c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RDATE, Events.RDATE); 4703c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXRULE, Events.EXRULE); 4704c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXDATE, Events.EXDATE); 4705c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 470634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4707c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, 4708c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_INSTANCE_TIME); 4709c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4710c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4711c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4712c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4713c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, 4714c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.GUESTS_CAN_INVITE_OTHERS); 4715c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4716c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4717c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4718c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DELETED, Events.DELETED); 471919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 472019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 472102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 472202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 472302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 472402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 472502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 472602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 47279ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 472802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 472902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 473002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 4731470aa5bc291ca33d51dda356f38ac2954026da9aAlon Albert sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY); 47329ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 4733fa332ecedc0c340109811552407142f6e4f600b2RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 473402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 473502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 473602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 473702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 473802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 473902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 474002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 474102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 474202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 474319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 47449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 47451b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 47469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 47479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 47489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 47499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 47509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 47519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 47529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 47539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 47549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 47569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 47579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 47589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 47599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 47609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 47619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 47629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 476302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 476402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 47659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 47679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 47689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 47699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 47709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 4771361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 4772361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 47739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 47749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 47759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 47769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 47779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 47789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 47799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 47809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 47819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4782315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4783315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4784315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4785315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4786315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 47879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 47889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 478964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 47909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 479164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * This is called by AccountManager when the set of accounts is updated. 479264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * <p> 479364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * We are overriding this since we need to delete from the 47949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 47957e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 47967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 479764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * 479864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * @param accounts The list of currently active accounts. 47999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4800f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik @Override 48019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 480264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Thread thread = new AccountsUpdatedThread(accounts); 480364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden thread.start(); 480464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 480564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 480664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private class AccountsUpdatedThread extends Thread { 480764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private Account[] mAccounts; 480864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 480964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountsUpdatedThread(Account[] accounts) { 481064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden mAccounts = accounts; 481164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 481264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 481364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden @Override 481464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden public void run() { 481564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // The process could be killed while the thread runs. Right now that isn't a problem, 481664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // because we'll just call removeStaleAccounts() again when the provider restarts, but 481764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // if we want to do additional actions we may need to use a service (e.g. start 481864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // EmptyService in onAccountsUpdated() and stop it when we finish here). 481964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 482064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 482164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(mAccounts); 482264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 482364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 482464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 482564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden /** 482664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * Makes sure there are no entries for accounts that no longer exist. 482764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden */ 482864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void removeStaleAccounts(Account[] accounts) { 4829ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4830ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4831ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4832ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4833ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4834ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 48359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 483646f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 48379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 48389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 48399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 48419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 48432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 48449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 48459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik for (String table : new String[]{Tables.CALENDARS, Tables.COLORS}) { 4847ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 48489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 48492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.rawQuery("SELECT DISTINCT " + 48502ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_NAME + 48517cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 48522ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + 48537cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 48549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 48554cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // ACCOUNT_TYPE_LOCAL is to store calendars not associated 48564cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // with a system account. Typically, a calendar must be 48574cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // associated with an account on the device or it will be 48584cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // deleted. 4859b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (c.getString(0) != null 4860b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && c.getString(1) != null 4861b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && !TextUtils.equals(c.getString(1), 4862b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.ACCOUNT_TYPE_LOCAL)) { 48639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 48649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 48659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 48669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 48702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = null; 48719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 48739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 4874f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 4875f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 4876f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 48779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 4878b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 48792f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // This will be a no-op for accounts without a color palette. 48802f251c778c06d21ed7693a70f4a1268ff929242eRoboErik mDb.execSQL(SQL_DELETE_FROM_COLORS, params); 48819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 48839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 48849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 48852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 48862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 48872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 48889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 48899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48903ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 48913ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 4892dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 48939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 48949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4895636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 4896636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 4897636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 4898636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 4899636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 4900636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 4901636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 4902636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 4903636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 4904636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 4905636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 4906636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 4907636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 4908636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 4909636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 4910636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 4911636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 4912636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 4913636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 49149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 4915