CalendarProvider2.java revision cad6bc946434363f6ba6fed58bfa818cd6736d21
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 20bf61571797b7b6a390d35f16aad7765ea348e5aeAndy McFaddenimport com.android.calendarcommon.DateException; 2193e0bbb921cce7a5cec355521bc570c03c9d6a1cAndy McFaddenimport com.android.calendarcommon.EventRecurrence; 22bf61571797b7b6a390d35f16aad7765ea348e5aeAndy McFaddenimport com.android.calendarcommon.RecurrenceProcessor; 2393e0bbb921cce7a5cec355521bc570c03c9d6a1cAndy McFaddenimport com.android.calendarcommon.RecurrenceSet; 247be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 257be45683e367bd6897daf6444b03be938f8f5eaaErikimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 26370f91c0cfe5a5fecaba6120e703f4d2271d2277Erikimport com.google.common.annotations.VisibleForTesting; 27370f91c0cfe5a5fecaba6120e703f4d2271d2277Erik 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 45a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 46a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 49b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract; 50b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Attendees; 51b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.CalendarAlerts; 52b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Calendars; 53b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Events; 54b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Instances; 55b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Reminders; 56b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.SyncState; 579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 581edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 59192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 62ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 643b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.io.File; 652ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErikimport java.lang.reflect.Array; 663b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.lang.reflect.Method; 679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 68ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 71dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 72bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFaddenimport java.util.Set; 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 74dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 7581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 79b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik * is defined in {@link android.provider.CalendarContract}. 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 848bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 85d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden static final boolean DEBUG_INSTANCES = false; 869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 877be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 88c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String ACCOUNT_SELECTION_PREFIX = Calendars.ACCOUNT_NAME + "=? AND " 89c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik + Calendars.ACCOUNT_TYPE + "=?"; 907be45683e367bd6897daf6444b03be938f8f5eaaErik 91f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final boolean PROFILE = false; 929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 938f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 101b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Events.ORIGINAL_ID, 102c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_SYNC_ID, 1039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1049ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 1059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 108b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_ID_INDEX = 3; 109b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4; 1107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees._ID, 1137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff Attendees.EVENT_ID, // Assume these are the same for each table 1147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1167e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 119646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 120646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 121646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 122646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 123646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 124646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 125646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 126646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 127646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 128646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 129646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 130646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 131646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 132646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 133646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 134646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 140ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 143f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik private CalendarInstancesHelper mInstancesHelper; 1449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1458ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // The extended property name for storing an Event original Timezone. 146f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // Due to an issue in Calendar Server restricting the length of the name we 147f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // had to strip it down 1488ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // TODO - Better name would be: 1498ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // "com.android.providers.calendar.CalendarSyncAdapter#originalTimezone" 1508ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio protected static final String EXT_PROP_ORIGINAL_TIMEZONE = 1518ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio "CalendarSyncAdapter#originalTimezone"; 1528ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 1533443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 154b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + ", " + 155b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTSTART_2445 + ", " + 156b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTEND_2445 + ", " + 1573443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1583443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 159b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 160b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1613443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 162b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 163b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 164b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " + 165b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 166c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik " SET " + Events.DIRTY + "=1" + 167b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 168b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 1692ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik protected static final String SQL_WHERE_ID = Events._ID + "=?"; 170b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EVENT_ID = "event_id=?"; 1714d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?"; 1724d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID = Events.ORIGINAL_ID + 1734d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden "=? AND " + Events._SYNC_ID + " IS NULL"; 174ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 175ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan private static final String SQL_WHERE_ATTENDEE_BASE = 176ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID 177ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan + " AND " + 178ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 179ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 180b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 181ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + SQL_WHERE_ATTENDEE_BASE; 182b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 183b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 184b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 185ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + 186ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan " AND " + 187ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 188b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 189b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 1902ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 191b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 192b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 193b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 1942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 195b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 196b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 197b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 198b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 199b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 200b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik Tables.EXTENDED_PROPERTIES + "." + CalendarContract.ExtendedProperties._ID + "=?"; 201b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 202b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 2032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik " WHERE " + Calendars.ACCOUNT_NAME + "=? AND " + 2042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + "=?"; 205b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 206fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 207fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 208fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 21881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 21981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 22081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 22181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 222b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 22381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 224b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 22581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 22618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 22718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 22818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 22918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 23018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 231b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 23218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 233b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")" + ") LEFT OUTER JOIN " + 23418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 23518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 236b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Attendees.EVENT_ID + "=" + 23718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 238b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 23918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 240b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 241b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.START_DAY + "<=? AND " + 242b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END_DAY + ">=?"; 24381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 244b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 245b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.BEGIN + "<=? AND " + 246b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END + ">=?"; 2479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 2552ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * The sort order is: events with an earlier start time occur first and if 2562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the start times are the same, then events with a later end time occur 2572ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * first. The later end time is ordered first so that long-running events in 2582ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the calendar views appear first. If the start and end times of two events 2592ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * are the same then we sort alphabetically on the title. This isn't 2602ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * required for correctness, it just adds a nice touch. 2612ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 2622ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; 2632ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 2642ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 2652ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * A regex for describing how we split search queries into tokens. Keeps 2662ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * quoted phrases as one token. "one \"two three\"" ==> ["one" "two three"] 267dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 268dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 269dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 270dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 271dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 272dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 273dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 274dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 275dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 276dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 27781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 278dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 279dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 280dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 281dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 282dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 283dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 284dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 285dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 286dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 28781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 28818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 28918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 29018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 29118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 29218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 293b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_EMAIL + ")"; 29418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 29518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 29618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 29718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 29818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 29918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 300b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_NAME + ")"; 30118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 30281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 303b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.TITLE, 304b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.DESCRIPTION, 305b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.EVENT_LOCATION, 30618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 30718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 30881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 30981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 310a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 311a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 312a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 313a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 314a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 315a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 316a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 317a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 318a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 319a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 320a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 321a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 322dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 323dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 324dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 325dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 326dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 327bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Set of columns allowed to be altered when creating an exception to a recurring event. */ 328bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final HashSet<String> ALLOWED_IN_EXCEPTION = new HashSet<String>(); 329bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden static { 330bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // _id, _sync_account, _sync_account_type, dirty, _sync_mark, calendar_id 331bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events._SYNC_ID); 332bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA1); 333bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA7); 33402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA3); 335bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.TITLE); 336bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION); 337bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION); 338bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.STATUS); 339c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS); 34002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6); 341bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DTSTART); 342c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // dtend -- set from duration as part of creating the exception 343bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_TIMEZONE); 344bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_END_TIMEZONE); 345bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DURATION); 346bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ALL_DAY); 347bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ACCESS_LEVEL); 348bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.AVAILABILITY); 349bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ALARM); 350bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_EXTENDED_PROPERTIES); 351bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RRULE); 352bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RDATE); 353bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXRULE); 354bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXDATE); 355bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_SYNC_ID); 356bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_INSTANCE_TIME); 357bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // originalAllDay, lastDate 358bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ATTENDEE_DATA); 359bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_MODIFY); 360bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_INVITE_OTHERS); 361bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_SEE_GUESTS); 362bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORGANIZER); 363bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // deleted, original_id, alerts 364bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 365bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 366bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Don't clone these from the base event into the exception event. */ 367bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final String[] DONT_CLONE_INTO_EXCEPTION = { 368bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events._SYNC_ID, 369bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA1, 37002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA2, 37102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA3, 37202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA4, 37302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA5, 37402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA6, 375bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA7, 37602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA8, 377c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA9, 378c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA10, 379bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden }; 380bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 381bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** set to 'true' to enable debug logging for recurrence exception code */ 382bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final boolean DEBUG_EXCEPTION = false; 383bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 384dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 385e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 386e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 3878bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 3888bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 389420b7fb569773ae573fbe90c3a9c522d4c368863Erik @VisibleForTesting 390420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected CalendarAlarmManager mCalendarAlarm; 391a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 392a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 393a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 394a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 395dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 396a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 397a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 398a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 399dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 400dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 401dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 402dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 403dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 404a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 405a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 406a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 407a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 421420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 425420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 427420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 4289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4388bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4398bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4408bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4418bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 442e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 443e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 444e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 445e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 446e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 447e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 448e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 4498bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4508bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 4539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 454ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 455ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 456ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 457f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 458f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 459f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 460ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 461ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 462ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 464ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 4658bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 4668bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 467dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 468e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 469e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 470ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 471ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4734caf8d015918f619a67d321a152f150a01022717Andy McFadden mMetaData = new MetaData(mDbHelper); 4744caf8d015918f619a67d321a152f150a01022717Andy McFadden mInstancesHelper = new CalendarInstancesHelper(mDbHelper, mMetaData); 4754caf8d015918f619a67d321a152f150a01022717Andy McFadden 4769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 4779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 4789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 4809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 4819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 4829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 4849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 4859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 4869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 487e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 4889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 489ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 490ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 491420b7fb569773ae573fbe90c3a9c522d4c368863Erik // This is pulled out for testing 492420b7fb569773ae573fbe90c3a9c522d4c368863Erik initCalendarAlarm(); 493e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 494e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 4958bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 4969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 4979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 499420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected void initCalendarAlarm() { 500420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = getOrCreateCalendarAlarmManager(); 501420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.getScheduleNextAlarmWakeLock(); 502e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 503e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 504e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 505420b7fb569773ae573fbe90c3a9c522d4c368863Erik if (mCalendarAlarm == null) { 506420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = new CalendarAlarmManager(mContext); 507e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 508420b7fb569773ae573fbe90c3a9c522d4c368863Erik return mCalendarAlarm; 509e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 510e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 511ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 512ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 513ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 514ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 515ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 516ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 517ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 518ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 519ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 520ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 521ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 522ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 523ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 524ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 525ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 526ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 52764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void verifyAccounts() { 52864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 52964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(AccountManager.get(getContext()).getAccounts()); 53064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 53164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 53264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 5339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 536315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 5379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 5389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 5399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 5419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 5449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 5469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 547ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 5489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 552315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 553315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 554315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 555315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 556315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 557315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 558315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 559315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 5609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 5619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 5629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 563315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 564ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 565315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 566315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 567a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 568315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 569315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 570315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 571ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 572315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 573315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 574ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 575315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 576ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 577ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 578ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 579ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 580420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 581ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 582ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 583f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 584f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 585f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 586ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 587ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 588ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 589ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 590ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 591f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 592f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 593f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 594ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 596ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 597ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 598315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 599ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 600ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 6013443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 602ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 603315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 604ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 605ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 606ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 607ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 608ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 609ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 610ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 6113443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 6123443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 613ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 614ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 615ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 616ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 617ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6183443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 619f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 620f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 621f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 622f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 623f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 624f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 625f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 626ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6273443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 628ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 629ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 630ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 631ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 632ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 633ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 634ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 635ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 636ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 637ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 638ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 639f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 640f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 641f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 642ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 643ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 644ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 645ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 646ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 647ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 648f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 649f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 650f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 651ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 652ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 653ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 654ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 655ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 656ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 657ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 658ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 660b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 661b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 662ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 663b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 664dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 665ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 666ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 667ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 668ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 669ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 671ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 672ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 673ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 674ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 675ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 676f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 677f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 678f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 679ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 680ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 6819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 682ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 683ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 684ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 685ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 686315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 687315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 688ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 690ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 691ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 692ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 69325e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 694ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 695315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 696315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 697ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 698ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 699f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 700f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 701f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 702ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 703ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 704ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 705315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 706315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String type = mCalendarCache.readTimezoneType(); 707315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return type.equals(CalendarCache.TIMEZONE_TYPE_HOME); 708315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 709315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 710ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 7119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 7129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 7139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 7149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 715315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 716315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7221f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7251f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7261f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7271f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7281f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7291f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7301f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 7312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* selection */, null, 7322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* sort */, 733d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 734315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 7352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik instancesTimezone, isHomeTimezone()); 7361f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 7371f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 7381f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 7391f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7401f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 7419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 742420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 7439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 747b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 7489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 7499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 750b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik mContentResolver.notifyChange(CalendarContract.CONTENT_URI, null, syncToNetwork); 7519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 7549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 7559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 756ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 757ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 7589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 7599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 7619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 7639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 7649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 765315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 7669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 7689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 7699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 7709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 7719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 7729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 7739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 7741ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 7769ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 7779ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 7789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 7799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 7801ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 7819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 782636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 783b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 7849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 78519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 78619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 78719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 78819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 7899ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 7909ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 79119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 79219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 79319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 79419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 795636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 796b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 79719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 79819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 7999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 80043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 801b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 8029ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendAccountFromParameterToSelection(selection, uri); 8039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 80543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 806b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 807636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 808b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 8139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 8149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 8169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 8189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 8229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 8249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 826315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 8272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceQuery(qb, begin, end, projection, selection, selectionArgs, 8282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik sortOrder, match == INSTANCES_BY_DAY, false /* don't force an expansion */, 829315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 83081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 83181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 83281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 83381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 83481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 83581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 83681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 83781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 83881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 83981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 84081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 84181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 84281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 84381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 844315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 84581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 84681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 8472ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceSearchQuery(qb, begin, end, query, projection, selection, 8482ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 849315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8506db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 8519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 8529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 8539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 8559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 8579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 8589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 8609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 8619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 8629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 8639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 865315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 866315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 867315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 8689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 86902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 871ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan qb.appendWhere(SQL_WHERE_ATTENDEE_BASE); 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 87402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 876636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 877b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 8789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 880b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 8819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 88302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 8849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 885636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 886b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 8879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 889b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 8909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 891b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 8929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 894b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 8959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 896b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 8979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 8989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 900b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 902636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 903b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 9049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 906b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 909b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 910636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 911b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 9129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 913315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 914b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 915315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 916315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 9179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 9189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 9199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 9239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 9279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 928ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 92939c65e5716e21e863d8de587d139dae85f99422fFred Quintana if (projection != null && projection.length == 1 93039c65e5716e21e863d8de587d139dae85f99422fFred Quintana && BaseColumns._COUNT.equals(projection[0])) { 93139c65e5716e21e863d8de587d139dae85f99422fFred Quintana qb.setProjectionMap(sCountProjectionMap); 93239c65e5716e21e863d8de587d139dae85f99422fFred Quintana } 93339c65e5716e21e863d8de587d139dae85f99422fFred Quintana 934ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 935ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 936ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 937ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 938ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 939ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 940ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 941ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 9429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 9439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 9449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 9459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 946b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik c.setNotificationUri(mContentResolver, CalendarContract.Events.CONTENT_URI); 9479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 9499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 9519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 9529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 9539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 9549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 9589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 9609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 9619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 962d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 963315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 964315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 9669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 9679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 9682ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik long rangeEnd, String[] projection, String selection, String[] selectionArgs, 9692ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String sort, boolean searchByDay, boolean forceExpansion, 9702ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 9719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 97281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 9739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 9759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 977315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 9789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 9809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 9819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 983315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 984315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 985b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 9869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 9879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 988315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 989315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 990b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 9919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9922ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 9932ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = new String[] {String.valueOf(rangeEnd), 9948335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 9952ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 9962ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 9972ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 9982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 9992ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 10002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 10012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10028335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 10037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 10049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 100681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 10072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * Combine a set of arrays in the order they are passed in. All arrays must 10082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * be of the same type. 10092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 10102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static <T> T[] combine(T[]... arrays) { 10112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (arrays.length == 0) { 10122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik throw new IllegalArgumentException("Must supply at least 1 array to combine"); 10132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int totalSize = 0; 10162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize += array.length; 10182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik T[] finalArray = (T[]) (Array.newInstance(arrays[0].getClass().getComponentType(), 10212ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize)); 10222ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int currentPos = 0; 10242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 10252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int length = array.length; 10262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik System.arraycopy(array, 0, finalArray, currentPos, length); 10272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik currentPos += array.length; 10282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return finalArray; 10302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 10312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 10322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 1033dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1034dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1035dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1036dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1037dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1038dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1039dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1040dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1041dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1042dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1043dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 104481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1045dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1046dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 104781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 104881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 104981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 105081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 105181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 105281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1053dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1054dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1055dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1056dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1057dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1058dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1059dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1060dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1061dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1062dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1063dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1064dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1065dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1066dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 106781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 106881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 106981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 107081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 107181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 107281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 107381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 107481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 107581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 107681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 107781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 107881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 107981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 108081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 108181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 108281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 108381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 108481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 108581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 1086cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * approximation of full-text search across multiple columns. The set 1087cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * of columns is specified by the SEARCH_COLUMNS constant. 1088cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * <p> 1089cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * Note the "WHERE" token isn't part of the returned string. The value 1090cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * may be passed into a query as the "HAVING" clause. 109181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 109281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 109381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 109481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 109581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 109681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 109781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 109881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 109981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 110081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 110181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 110281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1103dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1104dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1105dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 110681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 110781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 110881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 110981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 111118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 111218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 111318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 111481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 111681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 111781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 111881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 111981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] constructSearchArgs(String[] tokens, long rangeBegin, long rangeEnd) { 112018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 112118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numArgs = tokens.length * numCols + 2; 112281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // the additional two elements here are for begin/end time 112318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 112418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[0] = String.valueOf(rangeEnd); 112518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[1] = String.valueOf(rangeBegin); 112681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1127f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang int start = 2 + numCols * j; 1128f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 112918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 113081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 113181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 113281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 113381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 113481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 113581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 113681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 11372ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selection, String[] selectionArgs, String sort, boolean searchByDay, 11382ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 113918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 114081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 114181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1142dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 11432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = constructSearchArgs(tokens, rangeBegin, rangeEnd); 11442ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 11452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 11462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 11472ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // The appendWhere pieces get added first, so put the 11482ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik // newSelectionArgs first. 11492ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 11502ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 115118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 115218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1153dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1154dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 115681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 115781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1158315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 115981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 116081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 116181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 116281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 116381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 116418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 116518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 116656292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 116756292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1168315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1169315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1170315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 117156292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1172b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 117381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 117481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 117518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 117618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 117756292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 117856292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1179315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1180315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1181315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 118256292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1183b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 118481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 118581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 118618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 1187cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden Tables.EVENTS + "." + Instances._ID /* groupBy */, 1188cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden searchWhere /* having */, sort); 118981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 119081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 11916db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1192315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1193315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 119481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 11956db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 119643556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 119743556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1198315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1199192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 120043556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 120143556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1202192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 120343556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1204315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1205315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1206b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 12078335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 12088335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 12098335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 12106db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 12119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12159ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * table. Acquires the database lock and calls 12169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #acquireInstanceRangeLocked(long, long, boolean, boolean, String, boolean)}. 12179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1221d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1222315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1223315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1225d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1226315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1227315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 12289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 12299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1230315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1231315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 12329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 12339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 12349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 12359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 12399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 12409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 12419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 12429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 12439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 12449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1245315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1246315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1247315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 12489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1249420b7fb569773ae573fbe90c3a9c522d4c368863Erik void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1250315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 12519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 12529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 12539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1254d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1255d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "acquireInstanceRange begin=" + begin + " end=" + end + 1256d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " useMin=" + useMinimumExpansionWindow + " force=" + forceExpansion); 1257d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1258d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1259315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1260315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1261315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1262315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1263315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 12649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 12659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 12669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 12679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 12689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 12699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 12709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 12719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 12729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 12739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 12759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 12769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 12779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 12789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 12799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 12809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 12819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1282315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1283315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1284315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1285315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1286315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1287315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 12897be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 12907be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 12917be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 12927be45683e367bd6897daf6444b03be938f8f5eaaErik } 1293315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1294315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1295315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1296d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 1297d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1298d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "Wiping instances and expanding from scratch"); 1299d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1300d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 13019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1302b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1303f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13046db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 13059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 13069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1307f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1308315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1309315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 13109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1311315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 13127be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 13137be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 13147be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 13157be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 13167be45683e367bd6897daf6444b03be938f8f5eaaErik 13177be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 1318315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneType.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 13197be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 13207be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 13217be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 13227be45683e367bd6897daf6444b03be938f8f5eaaErik } 1323315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 13249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 13309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 13319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 13329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 13349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 13359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 13369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1339d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1340d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "instances are already expanded"); 1341d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1342f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1352f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 13549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 13579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 13589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1359f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 13619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1364315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 13659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 13689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 13699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 13709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 13719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 13729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 13739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 13749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 13759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 13769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 13789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 13799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 13809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 13876db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 138948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 139048587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1391315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1392315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 13939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 13949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 13959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1398b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /** 1399b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Determines if the event is recurrent, based on the provided values. 1400b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1401b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden public static boolean isRecurrenceEvent(String rrule, String rdate, String originalId, 1402b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId) { 1403b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return (!TextUtils.isEmpty(rrule) || 1404b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(rdate) || 1405b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalId) || 1406b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalSyncId)); 14079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1409646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1410646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1411d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 1412646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1413d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * corrects the fields DTSTART, DTEND, and DURATION if necessary. 1414646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1415d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values The values to check and correct 1416d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param modValues Any updates will be stored here. This may be the same object as 1417d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <strong>values</strong>. 1418646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1419646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1420d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean fixAllDayTime(ContentValues values, ContentValues modValues) { 1421499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden Integer allDayObj = values.getAsInteger(Events.ALL_DAY); 1422499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden if (allDayObj == null || allDayObj == 0) { 1423d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return false; 1424d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1425d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1426646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1427646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1428d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtstart = values.getAsLong(Events.DTSTART); 1429d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtend = values.getAsLong(Events.DTEND); 1430d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String duration = values.getAsString(Events.DURATION); 1431d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Time time = new Time(); 1432d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String tempValue; 1433d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1434d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Change dtstart so h,m,s are 0 if necessary. 1435d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.clear(Time.TIMEZONE_UTC); 1436d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtstart.longValue()); 1437d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1438d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.hour = 0; 1439d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.minute = 0; 1440d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.second = 0; 1441d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTSTART, time.toMillis(true)); 1442d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1443d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1444d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1445d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If dtend exists for this event make sure it's h,m,s are 0. 1446d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (dtend != null) { 1447646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1448d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtend.longValue()); 1449646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1450646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1451646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1452646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1453d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden dtend = time.toMillis(true); 1454d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTEND, dtend); 1455646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1456646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1457d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1458646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1459d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (duration != null) { 1460d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int len = duration.length(); 1461d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1462d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * in the seconds format, and if so converts it to days. 1463d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 1464d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (len == 0) { 1465d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = null; 1466d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else if (duration.charAt(0) == 'P' && 1467d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration.charAt(len - 1) == 'S') { 1468d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1469d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1470d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = "P" + days + "D"; 1471d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DURATION, duration); 1472d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1473646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1474646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1475d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1476646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1477646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1478646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1479bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1480bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1481bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Determines whether the strings in the set name columns that may be overridden 1482bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * when creating a recurring event exception. 1483bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1484bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This uses a white list because it screens out unknown columns and is a bit safer to 1485bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * maintain than a black list. 1486bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1487bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private void checkAllowedInException(Set<String> keys) { 1488bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : keys) { 1489bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!ALLOWED_IN_EXCEPTION.contains(str.intern())) { 1490bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions can't overwrite " + str); 1491bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1492bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1493bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1494bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1495bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 149632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Splits a recurrent event at a specified instance. This is useful when modifying "this 149732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * and all future events". 149832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 149932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence rule has a COUNT specified, we need to split that at the point of the 150032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * exception. If the exception is instance N (0-based), the original COUNT is reduced 150132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * to N, and the exception's COUNT is set to (COUNT - N). 150232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 150332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence doesn't have a COUNT, we need to update or introduce an UNTIL value, 150432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the original recurrence will end just before the exception instance. (Note 150532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * that UNTIL dates are inclusive.) 150632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 150732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * This should not be used to update the first instance ("update all events" action). 1508bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 150932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @param values The original event values; must include EVENT_TIMEZONE and DTSTART. 151032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * The RRULE value may be modified (with the expectation that this will propagate 151132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * into the exception event). 1512bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param endTimeMillis The time before which the event must end (i.e. the start time of the 1513bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event instance). 151432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @return Values to apply to the original event. 1515bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1516bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static ContentValues setRecurrenceEnd(ContentValues values, long endTimeMillis) { 151732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden boolean origAllDay = values.getAsBoolean(Events.ALL_DAY); 151832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden String origRrule = values.getAsString(Events.RRULE); 1519bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 152032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence origRecurrence = new EventRecurrence(); 152132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.parse(origRrule); 1522bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 152332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Get the start time of the first instance in the original recurrence. 152432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long startTimeMillis = values.getAsLong(Events.DTSTART); 1525bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time dtstart = new Time(); 1526bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = values.getAsString(Events.EVENT_TIMEZONE); 152732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.set(startTimeMillis); 1528bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1529bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues updateValues = new ContentValues(); 153032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 153132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origRecurrence.count > 0) { 153232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden /* 153332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Generate the full set of instances for this recurrence, from the first to the 153432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * one just before endTimeMillis. The list should never be empty, because this method 153532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * should not be called for the first instance. All we're really interested in is 153632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * the *number* of instances found. 153732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden */ 153832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceSet recurSet = new RecurrenceSet(values); 153932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceProcessor recurProc = new RecurrenceProcessor(); 154032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long[] recurrences; 154132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden try { 154232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden recurrences = recurProc.expand(dtstart, recurSet, startTimeMillis, endTimeMillis); 154332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } catch (DateException de) { 154432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException(de); 154532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 154632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 154732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (recurrences.length == 0) { 154832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException("can't use this method on first instance"); 154932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 155032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 155132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence excepRecurrence = new EventRecurrence(); 155232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden excepRecurrence.parse(origRrule); // TODO: add/use a copy constructor to EventRecurrence 155332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden excepRecurrence.count -= recurrences.length; 155432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden values.put(Events.RRULE, excepRecurrence.toString()); 155532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 155632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.count = recurrences.length; 155732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 155832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } else { 155932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden Time untilTime = new Time(); 156032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 156132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // The "until" time must be in UTC time in order for Google calendar 156232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // to display it properly. For all-day events, the "until" time string 156332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // must include just the date field, and not the time field. The 156432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // repeating events repeat up to and including the "until" time. 156532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.timezone = Time.TIMEZONE_UTC; 156632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 156732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Subtract one second from the exception begin time to get the "until" time. 156832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.set(endTimeMillis - 1000); // subtract one second (1000 millis) 156932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origAllDay) { 157032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.hour = untilTime.minute = untilTime.second = 0; 157132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.allDay = true; 157232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.normalize(false); 157332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 157432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // This should no longer be necessary -- DTSTART should already be in the correct 157532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // format for an all-day event. 157632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.hour = dtstart.minute = dtstart.second = 0; 157732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.allDay = true; 157832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.timezone = Time.TIMEZONE_UTC; 157932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 158032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.until = untilTime.format2445(); 158132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 158232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 158332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden updateValues.put(Events.RRULE, origRecurrence.toString()); 1584bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.DTSTART, dtstart.normalize(true)); 1585bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return updateValues; 1586bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1587bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1588bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1589bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Handles insertion of an exception to a recurring event. 1590bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1591bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * There are two modes, selected based on the presence of "rrule" in modValues: 1592bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <ol> 1593bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Create a single instance exception ("modify current event only"). 1594bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Cap the original event, and create a new recurring event ("modify this and all 1595bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * future events"). 1596bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * </ol> 1597bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This may be used for "modify all instances of the event" by simply selecting the 1598bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * very first instance as the exception target. In that case, the ID of the "new" 1599bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event will be the same as the originalEventId. 1600bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1601bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param originalEventId The _id of the event to be modified 1602bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param modValues Event columns to update 1603c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * @param callerIsSyncAdapter Set if the content provider client is the sync adapter 1604bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return the ID of the new "exception" event, or -1 on failure 1605bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1606c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden private long handleInsertException(long originalEventId, ContentValues modValues, 1607c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden boolean callerIsSyncAdapter) { 1608bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1609bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.i(TAG, "RE: values: " + modValues.toString()); 1610bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1611bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1612bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Make sure they have specified an instance via originalInstanceTime. 1613bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Long originalInstanceTime = modValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1614bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime == null) { 1615bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions must specify " + 1616bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.ORIGINAL_INSTANCE_TIME); 1617bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1618bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1619bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Check for attempts to override values that shouldn't be touched. 1620bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden checkAllowedInException(modValues.keySet()); 1621bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1622c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // If this isn't the sync adapter, set the "dirty" flag in any Event we modify. 1623c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!callerIsSyncAdapter) { 1624c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.put(Events.DIRTY, true); 1625c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1626c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1627bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Wrap all database accesses in a transaction. 1628bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.beginTransaction(); 1629bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Cursor cursor = null; 1630bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1631bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that there's an instance corresponding to the specified time 1632bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (does this matter? it's weird, but not fatal?) 1633bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1634bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Grab the full set of columns for this event. 1635bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor = mDb.query(Tables.EVENTS, null /* columns */, 1636bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(originalEventId) }, 1637bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1638bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor.getCount() != 1) { 1639bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event ID " + originalEventId + " lookup failed (count is " + 1640bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.getCount() + ")"); 1641bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1642bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1643bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden //DatabaseUtils.dumpCursor(cursor); 1644bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1645bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1646bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Verify that the original event is in fact a recurring event by checking for the 1647bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * presence of an RRULE. If it's there, we assume that the event is otherwise 1648bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * properly constructed (e.g. no DTEND). 1649bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1650bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.moveToFirst(); 1651bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int rruleCol = cursor.getColumnIndex(Events.RRULE); 1652bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (TextUtils.isEmpty(cursor.getString(rruleCol))) { 1653bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event has no rrule"); 1654bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1655bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1656bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1657bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: old RRULE is " + cursor.getString(rruleCol)); 1658bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1659bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1660bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Verify that the original event is not itself a (single-instance) exception. 1661bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int originalIdCol = cursor.getColumnIndex(Events.ORIGINAL_ID); 1662bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!TextUtils.isEmpty(cursor.getString(originalIdCol))) { 1663bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event is an exception"); 1664bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1665bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1666bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1667bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createSingleException = TextUtils.isEmpty(modValues.getAsString(Events.RRULE)); 1668bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1669bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: check for the presence of an existing exception on this event+instance? 1670bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The caller should be modifying that, not creating another exception. 1671bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Alternatively, we could do that for them.) 1672bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1673bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Create a new ContentValues for the new event. Start with the original event, 1674bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // and drop in the new caller-supplied values. This will set originalInstanceTime. 1675bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues values = new ContentValues(); 1676bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 1677bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1678b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: if we're changing this to an all-day event, we should ensure that 1679b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // hours/mins/secs on DTSTART are zeroed out (before computing DTEND). 1680b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // See fixAllDayTime(). 1681b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1682bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createNewEvent = true; 1683bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createSingleException) { 1684bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1685bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Save a copy of a few fields that will migrate to new places. 1686bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1687bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _id = values.getAsString(Events._ID); 1688bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _sync_id = values.getAsString(Events._SYNC_ID); 1689bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1690bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1691bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1692bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Wipe out some fields that we don't want to clone into the exception event. 1693bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1694bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : DONT_CLONE_INTO_EXCEPTION) { 1695bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(str); 1696bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1697bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1698bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1699bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Merge the new values on top of the existing values. Note this sets 1700bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * originalInstanceTime. 1701bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1702bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1703bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1704bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1705bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Copy some fields to their "original" counterparts: 1706bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id --> original_id 1707bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _sync_id --> original_sync_id 1708bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * allDay --> originalAllDay 1709bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1710bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this event hasn't been sync'ed with the server yet, the _sync_id field will 1711bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * be null. We will need to fill original_sync_id in later. (May not be able to 1712bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * do it right when our own _sync_id field gets populated, because the order of 1713bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * events from the server may not be what we want -- could update the exception 1714bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * before updating the original event.) 1715bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1716bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id is removed later (right before we write the event). 1717bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1718bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ID, _id); 1719bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_SYNC_ID, _sync_id); 1720bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ALL_DAY, allDay); 1721bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1722bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Mark the exception event status as "tentative", unless the caller has some 1723bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // other value in mind (like STATUS_CANCELED). 1724bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!values.containsKey(Events.STATUS)) { 1725bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.STATUS, Events.STATUS_TENTATIVE); 1726bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1727bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1728bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // We're converting from recurring to non-recurring. Clear out RRULE and replace 1729bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DURATION with DTEND. 1730c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.remove(Events.RRULE); 1731bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1732bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Duration duration = new Duration(); 1733bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String durationStr = values.getAsString(Events.DURATION); 1734bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1735bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden duration.parse(durationStr); 1736bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } catch (Exception ex) { 1737bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // NullPointerException if the original event had no duration. 1738bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DateException if the duration was malformed. 1739bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Bad duration in recurring event: " + durationStr, ex); 1740bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1741bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1742bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1743c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1744c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * We want to compute DTEND as an offset from the start time of the instance. 1745c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If the caller specified a new value for DTSTART, we want to use that; if not, 1746c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the DTSTART in "values" will be the start time of the first instance in the 1747c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * recurrence, so we want to replace it with ORIGINAL_INSTANCE_TIME. 1748c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1749c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long start; 1750c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.DTSTART)) { 1751c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.DTSTART); 1752c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1753c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1754c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.put(Events.DTSTART, start); 1755c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1756bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.DTEND, start + duration.getMillis()); 1757bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1758c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "RE: ORIG_INST_TIME=" + start + 1759c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ", duration=" + duration.getMillis() + 1760bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ", generated DTEND=" + values.getAsLong(Events.DTEND)); 1761bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 176285c09a31bcc3a18e173428bf7b628cec2834bebcAndy McFadden values.remove(Events.DURATION); 1763bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1764bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1765bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're going to "split" the recurring event, making the old one stop before 1766bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * this instance, and creating a new recurring event that starts here. 1767bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1768bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * No need to fill out the "original" fields -- the new event is not tied to 1769bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * the previous event in any way. 1770bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1771bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this is the first event in the series, we can just update the existing 1772bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event with the values. 1773bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1774bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean canceling = (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 1775bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1776bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime.equals(values.getAsLong(Events.DTSTART))) { 1777bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1778bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Update fields in the existing event. Rather than use the merged data 1779bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * from the cursor, we just do the update with the new value set after 1780bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * removing the ORIGINAL_INSTANCE_TIME entry. 1781bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1782bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (canceling) { 1783bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: should we just call deleteEventInternal? 1784bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "Note: canceling entire event via exception call"); 1785bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1786bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1787bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: updating full event"); 1788bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1789ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(modValues)) { 1790ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 1791ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 1792ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 1793bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden modValues.remove(Events.ORIGINAL_INSTANCE_TIME); 1794bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 1795bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1796bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden createNewEvent = false; // skip event creation and related-table cloning 1797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: splitting event"); 1800bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 180332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Cap the original event so it ends just before the target instance. In 180432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * some cases (nonzero COUNT) this will also update the RRULE in "values", 180532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the exception we're creating terminates appropriately. If a 180632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * new RRULE was specified by the caller, the new rule will overwrite our 180732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * changes when we merge the new values in below (which is the desired 180832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * behavior). 1809bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1810bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues splitValues = setRecurrenceEnd(values, originalInstanceTime); 1811bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, splitValues, SQL_WHERE_ID, 1812bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1813bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1814bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 181532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Prepare the new event. We remove originalInstanceTime, because we're now 1816bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * creating a new event rather than an exception. 1817bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1818bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're always cloning a non-exception event (we tested to make sure the 1819bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event doesn't specify original_id, and we don't allow original_id in the 1820bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * modValues), so we shouldn't end up creating a new event that looks like 1821bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * an exception. 1822bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1823bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1824bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events.ORIGINAL_INSTANCE_TIME); 1825bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1826c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1827bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1828bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long newEventId; 1829bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createNewEvent) { 1830bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events._ID); // don't try to set this explicitly 1831be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 1832be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, null); 1833be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 1834be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 1835be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 1836bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1837bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = mDb.insert(Tables.EVENTS, null, values); 1838bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (newEventId < 0) { 1839bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Unable to add exception to recurring event"); 1840bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Values: " + values); 1841bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1842bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1843bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1844bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: new ID is " + newEventId); 1845bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1846bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1847b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: do we need to do something like this? 1848b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden //updateEventRawTimesLocked(id, updatedValues); 1849b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1850b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1851b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Force re-computation of the Instances associated with the recurrence event. 1852b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1853b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, newEventId, true, mDb); 1854b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1855bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1856bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Some of the other tables (Attendees, Reminders, ExtendedProperties) reference 1857c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Event ID. We need to copy the entries from the old event, filling in the 1858c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * new event ID, so that somebody doing a SELECT on those tables will find 1859c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * matching entries. 1860bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1861bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden CalendarDatabaseHelper.copyEventRelatedTables(mDb, newEventId, originalEventId); 1862c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1863c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1864c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If we modified Event.selfAttendeeStatus, we need to keep the corresponding 1865c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * entry in the Attendees table in sync. 1866c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1867c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 1868c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1869c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * Each Attendee is identified by email address. To find the entry that 1870c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * corresponds to "self", we want to compare that address to the owner of 1871c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Calendar. We're expecting to find one matching entry in Attendees. 1872c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1873c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long calendarId = values.getAsLong(Events.CALENDAR_ID); 1874c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden cursor = mDb.query(Tables.CALENDARS, new String[] { Calendars.OWNER_ACCOUNT }, 1875c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(calendarId) }, 1876c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1877c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!cursor.moveToFirst()) { 1878c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.w(TAG, "Can't get calendar account_name for calendar " + calendarId); 1879c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1880c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden String accountName = cursor.getString(0); 1881c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ContentValues attValues = new ContentValues(); 1882c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.put(Attendees.ATTENDEE_STATUS, 1883c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.getAsString(Events.SELF_ATTENDEE_STATUS)); 1884c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1885c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (DEBUG_EXCEPTION) { 1886c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "Updating attendee status for event=" + newEventId + 1887c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden " name=" + accountName + " to " + 1888c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.getAsString(Attendees.ATTENDEE_STATUS)); 1889c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1890c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden int count = mDb.update(Tables.ATTENDEES, attValues, 1891c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Attendees.EVENT_ID + "=? AND " + Attendees.ATTENDEE_EMAIL + "=?", 1892c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden new String[] { String.valueOf(newEventId), accountName }); 1893b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (count != 1 && count != 2) { 1894b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // We're only expecting one matching entry. We might briefly see 1895b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // two during a server sync. 1896b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Log.e(TAG, "Attendee status update on event=" + newEventId + 1897b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden " name=" + accountName + " touched " + count + " rows"); 1898b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (false) { 1899b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This dumps PII in the log, don't ship with it enabled. 1900b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Cursor debugCursor = mDb.query(Tables.ATTENDEES, null, 1901b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.EVENT_ID + "=? AND " + 1902b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.ATTENDEE_EMAIL + "=?", 1903b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { String.valueOf(newEventId), accountName }, 1904b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden null, null, null); 1905b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden DatabaseUtils.dumpCursor(debugCursor); 1906b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden } 1907b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden throw new RuntimeException("Status update WTF"); 1908c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1909c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1910c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden cursor.close(); 1911c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1912bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1913b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1914b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Update any Instances changed by the update to this Event. 1915b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1916b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, originalEventId, false, mDb); 1917bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = originalEventId; 1918bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1919bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1920bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.setTransactionSuccessful(); 1921bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return newEventId; 1922bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } finally { 1923bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor != null) { 1924bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.close(); 1925bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1926bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.endTransaction(); 1927bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1928bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1929bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1930222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden /** 1931222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Fills in the originalId column for previously-created exceptions to this event. If 1932222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this event is not recurring or does not have a _sync_id, this does nothing. 1933222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 1934222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * The server might send exceptions before the event they refer to. When 1935222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this happens, the originalId field will not have been set in the 1936222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * exception events (it's the recurrence events' _id field, so it can't be 1937222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * known until the recurrence event is created). When we add a recurrence 1938222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * event with a non-empty _sync_id field, we write that event's _id to the 1939222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * originalId field of any events whose originalSyncId matches _sync_id. 1940222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 1941222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Note _sync_id is only expected to be unique within a particular calendar. 1942222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * 1943222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param id The ID of the Event 1944222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param values Values for the Event being inserted 1945222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden */ 1946222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden private void backfillExceptionOriginalIds(long id, ContentValues values) { 1947222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String syncId = values.getAsString(Events._SYNC_ID); 1948222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rrule = values.getAsString(Events.RRULE); 1949222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rdate = values.getAsString(Events.RDATE); 1950222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String calendarId = values.getAsString(Events.CALENDAR_ID); 1951222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 1952222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden if (TextUtils.isEmpty(syncId) || TextUtils.isEmpty(calendarId) || 1953222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden (TextUtils.isEmpty(rrule) && TextUtils.isEmpty(rdate))) { 1954222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden // Not a recurring event, or doesn't have a server-provided sync ID. 1955222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden return; 1956222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 1957222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 1958222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden ContentValues originalValues = new ContentValues(); 1959222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden originalValues.put(Events.ORIGINAL_ID, id); 1960222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden mDb.update(Tables.EVENTS, originalValues, 1961222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden Events.ORIGINAL_SYNC_ID + "=? AND " + Events.CALENDAR_ID + "=?", 1962222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden new String[] { syncId, calendarId }); 1963222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 1964222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 19659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 1966b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 1967ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 19689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 19699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 19710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_INSERT, uri, values, callerIsSyncAdapter, match, 19720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik null /* selection */, null /* selection args */); 19739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 19759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 19769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 1977bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case SYNCSTATE: 19789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 19799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 19809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 19817e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 1982c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 19837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 19849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 19859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 19869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 19879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 1988e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 1989be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 1990be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(updatedValues, null); 1991be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 1992be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(updatedValues); 1993be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 1994e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 1995e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 19969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 19979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 19989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 19999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 20019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues.containsKey(Events.CALENDAR_ID) && 20029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff !updatedValues.containsKey(Events.ORGANIZER)) { 20039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 20049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 20059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 20069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 20079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 20089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 20099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 20109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 201234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 201334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !updatedValues.containsKey(Events.ORIGINAL_ID)) { 201434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = getOriginalId(updatedValues 201534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsString(Events.ORIGINAL_SYNC_ID)); 201634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId != -1) { 201734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_ID, originalId); 201834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 201934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } else if (!updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 202034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && updatedValues.containsKey(Events.ORIGINAL_ID)) { 202134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = getOriginalSyncId(updatedValues 202234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsLong(Events.ORIGINAL_ID)); 202334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (!TextUtils.isEmpty(originalSyncId)) { 202434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_SYNC_ID, originalSyncId); 202534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 202634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 2027d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(updatedValues, updatedValues)) { 2028f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2029f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 2030f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 2031f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2032646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 2033c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 20349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 20359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 20369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 2037f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 2038f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik true /* new event */, mDb); 20399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 20409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 20419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 20429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 20439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 20449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 20459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID)); 20469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 20489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20498ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // if the Event Timezone is defined, store it as the original one in the 20508ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // ExtendedProperties table 20518ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) { 20528ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE); 20538ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20548ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues expropsValues = new ContentValues(); 2055b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.EVENT_ID, id); 2056b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.NAME, 20578ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio EXT_PROP_ORIGINAL_TIMEZONE); 2058b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik expropsValues.put(CalendarContract.ExtendedProperties.VALUE, 2059b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik originalTimezone); 20608ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio 20618ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Insert the extended property 20628ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues); 20638ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (exPropId == -1) { 20648ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20658ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot add the original Timezone in the " 20668ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + "ExtendedProperties table for Event: " + id); 20678ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20688ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } else { 20698ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio // Update the Event for saying it has some extended properties 20708ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio ContentValues eventValues = new ContentValues(); 20718ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1"); 2072b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update("Events", eventValues, SQL_WHERE_ID, 20738ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio new String[] {String.valueOf(id)}); 20748ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (result <= 0) { 20758ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 20768ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio Log.e(TAG, "Cannot update hasExtendedProperties column" 20778ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio + " for Event: " + id); 20788ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20798ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20808ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 20818ba0d0238b153d331d612078b19492cb44728101Fabrice Di Meglio } 2082b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 2083222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden backfillExceptionOriginalIds(id, values); 2084222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2085dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 20869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 20879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2088bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID: 2089bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long originalEventId = ContentUris.parseId(uri); 2090c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden id = handleInsertException(originalEventId, values, callerIsSyncAdapter); 2091bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden break; 20929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 20939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 20949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 2095c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 20969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 2097c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 20989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 2099fa332ecedc0c340109811552407142f6e4f600b2RoboErik String eventsUrl = values.getAsString(Calendars.CAL_SYNC1); 21001b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 21019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2103dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 21049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 21069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 21119ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Attendees.EVENT_ID); 21129ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 21139ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 21147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21159ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.attendeesInsert(values); 21169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 21199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 21219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Reminders.EVENT_ID)) { 21229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 21239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 21269ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Reminders.EVENT_ID); 21279ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 21289ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 21297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21309ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.remindersInsert(values); 21319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 21339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 21349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 21359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2136420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 21379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 21399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(CalendarAlerts.EVENT_ID)) { 21409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 21442fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 21452fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 21469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 2148b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (!values.containsKey(CalendarContract.ExtendedProperties.EVENT_ID)) { 21499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 21509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 21519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2153b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik final Long eventId = values 2154b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik .getAsLong(CalendarContract.ExtendedProperties.EVENT_ID); 21559ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 21569ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 21577e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21589ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.extendedPropertiesInsert(values); 21599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21603b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden case EMMA: 21613b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Special target used during code-coverage evaluation. 21623b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden handleEmmaRequest(values); 21633b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden break; 21649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 21659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 21669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 21679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 21689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 21699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 21706db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2171315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 21727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 21739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 21749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 21759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 21789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 21799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 21829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2184e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 21853b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Handles special commands related to EMMA code-coverage testing. 21863b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 21873b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * @param values Parameters from the caller. 21883b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 21893b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static void handleEmmaRequest(ContentValues values) { 21903b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /* 21913b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * This is not part of the public API, so we can't share constants with the CTS 21923b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * test code. 21933b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 21943b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Bad requests, or attempting to request EMMA coverage data when the coverage libs 21953b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * aren't linked in, will cause an exception. 21963b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 21973b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String cmd = values.getAsString("cmd"); 21983b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden if (cmd.equals("start")) { 21993b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // We'd like to reset the coverage data, but according to FAQ item 3.14 at 22003b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // http://emma.sourceforge.net/faq.html, this isn't possible in 2.0. 22013b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage testing started"); 22023b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } else if (cmd.equals("stop")) { 22033b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Call com.vladium.emma.rt.RT.dumpCoverageData() to cause a data dump. We 22043b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // may not have been built with EMMA, so we need to do this through reflection. 22053b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String filename = values.getAsString("outputFileName"); 22063b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 22073b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden File coverageFile = new File(filename); 22083b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden try { 22093b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); 22103b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", 22113b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden coverageFile.getClass(), boolean.class, boolean.class); 22123b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 22133b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden dumpCoverageMethod.invoke(null, coverageFile, false /*merge*/, 22143b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden false /*stopDataCollection*/); 22153b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage data written to " + filename); 22163b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } catch (Exception e) { 22173b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden throw new RuntimeException("Emma coverage dump failed", e); 22183b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 22193b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 22203b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 22213b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 22223b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /** 22235ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * Validates the recurrence rule, if any. We allow single- and multi-rule RRULEs. 2224ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * <p> 22255ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * TODO: Validate RDATE, EXRULE, EXDATE (possibly passing in an indication of whether we 22265ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * believe we have the full set, so we can reject EXRULE when not accompanied by RRULE). 2227ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * 2228ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * @return A boolean indicating successful validation. 2229ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden */ 2230ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden private boolean validateRecurrenceRule(ContentValues values) { 2231ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden String rrule = values.getAsString(Events.RRULE); 2232ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2233ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!TextUtils.isEmpty(rrule)) { 22345ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden String[] ruleList = rrule.split("\n"); 22355ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden for (String recur : ruleList) { 22365ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden EventRecurrence er = new EventRecurrence(); 22375ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden try { 22385ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden er.parse(recur); 22395ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } catch (EventRecurrence.InvalidFormatException ife) { 22405ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden Log.w(TAG, "Invalid recurrence rule: " + recur); 22415ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden return false; 22425ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } 2243ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2244ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2245ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2246ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden return true; 2247ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2248ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2249ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden /** 225062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Do some scrubbing on event data before inserting or updating. In particular make 225162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * dtend, duration, etc make sense for the type of event (regular, recurrence, exception). 225262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Remove any unexpected fields. 2253e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 225462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param values the ContentValues to insert. 225562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param modValues if non-null, explicit null entries will be added here whenever something 225662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * is removed from <strong>values</strong>. 2257e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 225862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden private void scrubEventData(ContentValues values, ContentValues modValues) { 2259e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2260e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2261e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2262e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2263c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2264e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2265e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2266e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2267e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2268e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2269e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2270ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden // rrule is a valid recurrence rule 2271e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2272e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2273e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2274ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(values)) { 2275ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2276ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 2277ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2278e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 227962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DTEND, ORIGINAL_SYNC_ID, ORIGINAL_INSTANCE_TIME"); 2280e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 228162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence: " + values); 2282e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2283e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2284c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.remove(Events.ORIGINAL_SYNC_ID); 2285e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 228662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 228762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DTEND); 228862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_SYNC_ID); 228962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_INSTANCE_TIME); 229062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2291e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2292e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2293e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2294e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2295e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2296e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2297e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2298e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2299e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2300e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2301e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 230262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2303e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 230462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence exception: " + values); 2305e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2306e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 230762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 230862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 230962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2310e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2311e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2312e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2313e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2314e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2315e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2316e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2317e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2318e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2319e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2320e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 232162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2322e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 232362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for event: " + values); 2324e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2325e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 232662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 232762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 232862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2329e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2330e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2331e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2332e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 2333d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2334d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Validates event data. Pass in the full set of values for the event (i.e. not just 2335d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * a part that's being updated). 2336d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2337d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values Event data. 2338d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws IllegalArgumentException if bad data is found. 2339d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 2340d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private void validateEventData(ContentValues values) { 2341d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtstart = values.getAsLong(Events.DTSTART) != null; 2342d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2343d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2344d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2345d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2346d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasCalId = !TextUtils.isEmpty(values.getAsString(Events.CALENDAR_ID)); 2347d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasCalId) { 2348d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("New events must include a calendar_id."); 2349d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2350d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasRrule || hasRdate) { 2351d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!validateRecurrenceRule(values)) { 2352d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2353d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.getAsString(Events.RRULE)); 2354d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2355d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2356d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 2357d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDtstart) { 2358d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTSTART cannot be empty."); 2359d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2360d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDuration && !hasDtend) { 2361d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 2362d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "an event."); 2363d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2364d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasDuration && hasDtend) { 2365d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Cannot have both DTEND and DURATION in an event"); 2366d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2367d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2368d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 23699ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private void setEventDirty(long eventId) { 23709ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Object[] {eventId}); 23717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 237334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private long getOriginalId(String originalSyncId) { 237434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (TextUtils.isEmpty(originalSyncId)) { 237534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return -1; 237634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 237734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 237834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = -1; 237934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 238034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 238134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, 238234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._SYNC_ID + "=?", new String[] {originalSyncId}, null); 238334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 238434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalId = c.getLong(0); 238534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 238634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 238734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 238834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 238934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 239034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 239134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalId; 239234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 239334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 239434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private String getOriginalSyncId(long originalId) { 239534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId == -1) { 239634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return null; 239734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 239834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 239934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = null; 240034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 240134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 240234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, new String[] {Events._SYNC_ID}, 240334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._ID + "=?", new String[] {Long.toString(originalId)}, null); 240434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 240534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalSyncId = c.getString(0); 240634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 240734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 240834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 240934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 241034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 241134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 241234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalSyncId; 241334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 241434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 24159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Gets the calendar's owner for an event. 24179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param calId 24189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 24199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2421f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2422f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2423f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2424f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2425f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2426f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 24279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 24289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 24299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 24309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 24319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 24329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 24339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 24349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 24359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 24369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2437f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2438f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2439f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 24409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 24419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 24439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 24449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 24459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 24469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 24499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 24539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 24549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 24569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 24579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 24589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 24599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 24619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 24629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 24639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 24649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 24659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 24669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 24679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 24689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 24699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 24709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 24729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 24739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 24749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 24759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 24779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 24789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 24799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 24809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 24819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param attendeeValues the column values for one row in the Attendees 24829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. 24839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 24849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 24859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 24869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long eventId = attendeeValues.getAsLong(Attendees.EVENT_ID); 24879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 24889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 24899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 24909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 24919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 24929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 24939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 24949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 24959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 24969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 24979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 24989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2499f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2500f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2501f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 25029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 25059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 25069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 25079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 25089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 25129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 25139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 25149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 25159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 25169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 25179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 25189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 25199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 25209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2521f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2522f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2523f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 25249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 25279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 25289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 25299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 25309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 25349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 25389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 25399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 25409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 25419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 25449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 25459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 25469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 25479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 25489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 25529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_RELATIONSHIP)) { 25539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int rel = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 25549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 25559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 25569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_STATUS)) { 25609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 25619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 25649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2565b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2566b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 25679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2569d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2570d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Calculates the "last date" of the event. For a regular event this is the start time 2571d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * plus the duration. For a recurring event this is the start date of the last event in 2572d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence, plus the duration. The event recurs forever, this returns -1. If 2573d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence rule can't be parsed, this returns -1. 2574d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2575d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values 2576d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the date, in milliseconds, since the start of the epoch (UTC), or -1 if an 2577d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * exceptional condition exists. 2578d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws DateException 2579d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 25809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 25819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 25829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 25839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 25849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 25859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 25869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 25879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 25889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 25899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 25909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 25919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 25929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 25949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 25959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 25969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 25979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 25989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 25999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 26009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 26019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 26029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 26039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 26049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 26059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 26069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 26079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 26089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 26099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 26109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 26119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 26129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2614f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2615f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2616f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2617f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2618f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2619f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2620b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik values.get(CalendarContract.Events.RRULE), e); 2621f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2622d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: this should throw an exception or return a distinct error code 2623f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2624f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 26259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2626f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 26279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 26289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 26299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 26319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 26339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 26349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 26359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 26379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 26399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 26419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 26429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 2643d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // repeats forever 26449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 26459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 26479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 26489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 26499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 26529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 26539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 26559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2657e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2658e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 2659e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @param values the ContentValues (in/out) 2660e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2661e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2662e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 26639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 26649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 26659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 26669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 26679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 26709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 26719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 2672f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2673f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 2674f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 26759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 26769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2679d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2680d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Creates or updates an entry in the EventsRawTimes table. 2681d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2682d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param eventId The ID of the event that was just created or is being updated. 2683d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values For a new event, the full set of event values; for an updated event, 2684d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the set of values that are being changed. 2685d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 26869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 26879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 26889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2689b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.EVENT_ID, eventId); 26909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 26929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 26959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 26969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 26979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 27019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 27029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 27059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 27069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 2709b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTSTART_2445, time.format2445()); 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 27149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 2715b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTEND_2445, time.format2445()); 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 27199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 2728b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.ORIGINAL_INSTANCE_TIME_2445, 2729b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 27339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 27349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 27359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 2736b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.LAST_DATE_2445, time.format2445()); 27379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 27409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2743b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 2744b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 2745ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 27469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 27479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 27490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_DELETE, uri, null, callerIsSyncAdapter, match, 27500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 27510739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 27529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 27539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 27572ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 27589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 27599323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 2760dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 2761dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 2762dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 2763dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27651ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 27669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 27677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 27680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection = appendSyncAccountToSelection(uri, selection); 27697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 27701ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 2771ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION, 27721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 27737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 27749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27751ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 27761ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 277710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 27789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2779420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 2780dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 27819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 27849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 27869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27871ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 27881ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 27891ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 27901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (selection != null) { 27911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff throw new UnsupportedOperationException("CalendarProvider2 " 27921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + "doesn't support selection based deletion for type " 27931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff + match); 27941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 279510b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 27961ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 2797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID2: 2798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden { 2799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // This will throw NumberFormatException on missing or malformed input. 2800bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden List<String> segments = uri.getPathSegments(); 2801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long eventId = Long.parseLong(segments.get(1)); 2802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long excepId = Long.parseLong(segments.get(2)); 2803bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that this is an exception instance (has an ORIGINAL_ID field 2804bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // that matches the supplied eventId) 2805bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return deleteEventInternal(excepId, callerIsSyncAdapter, false /* isBatch */); 2806bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 28079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 28089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2810b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 28117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2812b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, selection, selectionArgs); 28137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 28169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28172fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28182fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28192fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2822b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 2823b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 28247e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2825b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.ATTENDEES, uri, null /* selection */, 28262fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 28309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2832b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, selection, selectionArgs); 28337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2834b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, selection, selectionArgs); 28357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28392fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28402fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28412fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 28437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 2844b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.REMINDERS, SQL_WHERE_ID, 2845b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 28467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2847b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.REMINDERS, uri, null /* selection */, 28482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28502fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28512fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 28522fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 28532fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 2854b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 28552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2856b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, selection, 2857b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 28582fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 28612fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 28622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28632fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28642fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28652fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 28662fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 2867b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 2868636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 28692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 2870b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.EXTENDED_PROPERTIES, uri, null /* selection */, 28712fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 28727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 28759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28767e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 2877b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 28787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 2879b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return deleteFromTable(Tables.CALENDAR_ALERTS, uri, selection, selectionArgs); 28807e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 28819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 28839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 28842fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 28852fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 28862fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 28872fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 28882fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 28899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 2890b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 2891b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 28929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 28942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik StringBuilder selectionSb = new StringBuilder(Calendars._ID + "="); 28959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 28969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 28979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 28989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 28999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 29009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 29029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // fall through to CALENDARS for the actual delete 29039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 2904595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff selection = appendAccountToSelection(uri, selection); 290574ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 29069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 29079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 29086db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2909315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 29109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 29119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 29129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 29139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 291610b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 29171ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 2918192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 29191ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29201ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 2921b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 2922b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 2923636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 29241ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 29251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 29261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 29271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 29281ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 292948f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 29301ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 29311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 29321ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 29331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 29341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 29351ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 2936b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origId = cursor.getString(EVENTS_ORIGINAL_ID_INDEX); 2937b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origSyncId = cursor.getString(EVENTS_ORIGINAL_SYNC_ID_INDEX); 2938b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (isRecurrenceEvent(rrule, rdate, origId, origSyncId)) { 29391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 29401ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29414d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden boolean isRecurrence = !TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate); 29421ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 294348f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 294448f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 2945bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // 2946bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The EVENTS_CLEANUP_TRIGGER_SQL trigger will remove all associated data 2947bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Attendees, Instances, Reminders, etc). 294848f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 2949b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 29504d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 29514d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // If this is a recurrence, and the event was never synced with the server, 29524d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we want to delete any exceptions as well. (If it has been to the server, 29534d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we'll let the sync adapter delete the events explicitly.) We assume that, 29544d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // if the recurrence hasn't been synced, the exceptions haven't either. 29554d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden if (isRecurrence && emptySyncId) { 29564d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID, selectionArgs); 29574d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden } 29581ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 2959bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Event is on the server, so we "soft delete", i.e. mark as deleted so that 2960bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the sync adapter has a chance to tell the server about the deletion. After 2961bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the server sees the change, the sync adapter will do the "hard delete" 2962bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (above). 29631ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 29641b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 2965c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 2966b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 296702494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 29684d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // Exceptions that have been synced shouldn't be deleted -- the sync 29694d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // adapter will take care of that -- but we want to "soft delete" them so 29704d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // that they will be removed from the instances list. 29714d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // TODO: this seems to confuse the sync adapter, and leaves you with an 29724d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // invisible "ghost" event after the server sync. Maybe we can fix 29734d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // this by making instance generation smarter? Not vital, since the 29744d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exception instances disappear after the server sync. 29754d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden //mDb.update(Tables.EVENTS, values, SQL_WHERE_ORIGINAL_ID_HAS_SYNC_ID, 29764d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // selectionArgs); 29774d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 29784d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // It's possible for the original event to be on the server but have 29794d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exceptions that aren't. We want to remove all events with a matching 29804d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // original_id and an empty _sync_id. 29814d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID, 29824d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden selectionArgs); 29834d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 298443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 298543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 2986b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 2987b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 2988b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 2989b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 2990b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 2991b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 29921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29931ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29941ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 29951ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 29961ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 29971ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 29988f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 299910b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 3000420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3001dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 300210b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 30031ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 30041ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 30051ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 30067e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 30077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Delete rows from a table and mark corresponding events as dirty. 30087e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 30097e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 30107e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 30117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 30127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 30137e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int deleteFromTable(String table, Uri uri, String selection, String[] selectionArgs) { 30147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 30157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 30169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 30179ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final ContentValues values = new ContentValues(); 3018c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, "1"); 30197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 30207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 30217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 30229ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long id = c.getLong(ID_INDEX); 30239ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long event_id = c.getLong(EVENT_ID_INDEX); 30249ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(event_id); 30259ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.delete(table, SQL_WHERE_ID, new String[]{String.valueOf(id)}); 3026b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 3027b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 30287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 30297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30307e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 30317e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 30327e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30337e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 30347e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 30367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 30377e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * Update rows in a table and mark corresponding events as dirty. 30387e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 30397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param values The values to update 30407e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 30417e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 30427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 30437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 30447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private int updateInTable(String table, ContentValues values, Uri uri, String selection, 30457e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff String[] selectionArgs) { 30467e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // Note that the query will return data according to the access restrictions, 30477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff // so we don't need to worry about deleting data we don't have permission to read. 30489ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, null); 30499ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final ContentValues dirtyValues = new ContentValues(); 3050c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik dirtyValues.put(Events.DIRTY, "1"); 30517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 30527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 30537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff while(c.moveToNext()) { 30549ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long id = c.getLong(ID_INDEX); 30559ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final long event_id = c.getLong(EVENT_ID_INDEX); 30569ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(event_id); 3057b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(table, values, SQL_WHERE_ID, new String[] {String.valueOf(id)}); 3058b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 3059b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(event_id)}); 30607e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 30617e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 30637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 30647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 30667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 30677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 306874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 30699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 30709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 307274ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 307374ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 307474ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 307574ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 307674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 30779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 30789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 30799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 30819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 30829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 30839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 30849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 30869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 30879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 308874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 30899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3091fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 3092fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 3093fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3094fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 3095fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3096fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3097fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3098fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 3099fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 3100fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 3101fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3102fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3103fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 3104fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 3105fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3106fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 3107fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 3108fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 3109fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3110fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 3111fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 3112fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 3113d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean doesStatusCancelUpdateMeanUpdate(ContentValues values, 3114d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues modValues) { 3115d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isStatusCanceled = modValues.containsKey(Events.STATUS) && 3116d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden (modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 3117fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 3118d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String originalSyncId = values.getAsString(Events.ORIGINAL_SYNC_ID); 3119d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3120d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!TextUtils.isEmpty(originalSyncId)) { 3121d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This event is an exception. See if the recurring event still exists. 3122d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return doesEventExistForSyncId(originalSyncId); 3123d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3124d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3125d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This is the normal case, we just want an UPDATE 3126d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return true; 3127d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3128d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3129d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3130d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 3131d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Handles a request to update one or more events. 3132d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 3133d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * The original event(s) will be loaded from the database, merged with the new values, 3134d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * and the result checked for validity. In some cases this will alter the supplied 3135d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * arguments (e.g. zeroing out the times on all-day events), change additional fields (e.g. 3136d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * update LAST_DATE when DTSTART changes), or cause modifications to other tables (e.g. reset 3137d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Instances when a recurrence rule changes). 3138d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3139d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param cursor The set of events to update. 31404b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden * @param updateValues The changes to apply to each event. 3141d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param callerIsSyncAdapter Indicates if the request comes from the sync adapter. 3142d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the number of rows updated 3143d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 31444b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden private int handleUpdateEvents(Cursor cursor, ContentValues updateValues, 3145d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean callerIsSyncAdapter) { 3146d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* 3147d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For a single event, we can just load the event, merge modValues in, perform any 3148d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * fix-ups (putting changes into modValues), check validity, and then update(). We have 3149d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * to be careful that our fix-ups don't confuse the sync adapter. 3150d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3151d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For multiple events, we need to load, merge, and validate each event individually. 3152d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * If no single-event-specific changes need to be made, we could just issue the original 3153d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * bulk update, which would be more efficient than a series of individual updates. 3154d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * However, doing so would prevent us from taking advantage of the partial-update 3155d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * mechanism. 3156d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 3157d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (cursor.getCount() > 1) { 3158d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3159d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "Performing update on " + cursor.getCount() + " events"); 3160d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3161d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3162d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden while (cursor.moveToNext()) { 31639f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Make a copy of updateValues so we can make some local changes. 31644b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden ContentValues modValues = new ContentValues(updateValues); 31659f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 31669f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Load the event into a ContentValues object. 3167d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues values = new ContentValues(); 3168d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 31699f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden boolean doValidate = false; 31709f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (!callerIsSyncAdapter) { 31719f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden try { 31729f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Check to see if the data in the database is valid. If not, we will skip 31739f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // validation of the update, so that we don't blow up on attempts to 31749f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // modify existing badly-formed events. 31759f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden validateEventData(values); 31769f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden doValidate = true; 31779f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } catch (IllegalArgumentException iae) { 31789f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden Log.d(TAG, "Event " + values.getAsString(Events._ID) + 31799f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden " malformed, not validating update (" + 31809f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden iae.getMessage() + ")"); 31819f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 31829f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 31839f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 31849f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Merge the modifications in. 3185d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.putAll(modValues); 3186d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 31879f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Scrub and/or validate the combined event. 3188be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 3189be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, modValues); 31909f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 31919f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (doValidate) { 3192be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 3193be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 3194d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3195d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Look for any updates that could affect LAST_DATE. It's defined as the end of 3196d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // the last meeting, so we need to pay attention to DURATION. 3197d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3198d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DTEND) || 3199d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DURATION) || 3200d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EVENT_TIMEZONE) || 3201d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RRULE) || 3202d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RDATE) || 3203d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXRULE) || 3204d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXDATE)) { 3205d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long newLastDate; 3206d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3207d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden newLastDate = calculateLastDate(values); 3208d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } catch (DateException de) { 3209d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Unable to compute LAST_DATE", de); 3210d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3211d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long oldLastDateObj = values.getAsLong(Events.LAST_DATE); 3212d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long oldLastDate = (oldLastDateObj == null) ? -1 : oldLastDateObj; 3213d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (oldLastDate != newLastDate) { 3214d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This overwrites any caller-supplied LAST_DATE. This is okay, because the 3215d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // caller isn't supposed to be messing with the LAST_DATE field. 3216d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (newLastDate < 0) { 3217d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.putNull(Events.LAST_DATE); 3218d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3219d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.LAST_DATE, newLastDate); 3220fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3221fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3222d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3223d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3224d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3225d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DIRTY, 1); 3226d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3227fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3228d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Disallow updating the attendee status in the Events 3229d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // table. In the future, we could support this but we 3230d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // would have to query and update the attendees table 3231d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to keep the values consistent. 3232d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 3233d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Updating " 3234d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + Events.SELF_ATTENDEE_STATUS 3235d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + " in Events table is not allowed."); 3236d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3237d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3238d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(values, modValues)) { 3239d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.WARN)) { 3240d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.w(TAG, "handleUpdateEvents: " + 3241d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "allDay is true but sec, min, hour were not 0."); 3242fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3243d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3244d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3245d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // For taking care about recurrences exceptions cancelations, check if this needs 3246d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to be an UPDATE or a DELETE 3247d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isUpdate = doesStatusCancelUpdateMeanUpdate(values, modValues); 3248d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3249d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = values.getAsLong(Events._ID); 3250d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3251d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (isUpdate) { 3252d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If a user made a change, possibly duplicate the event so we can do a partial 3253d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // update. If a sync adapter made a change and that change marks an event as 3254d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // un-dirty, remove any duplicates that may have been created earlier. 3255d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3256d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.duplicateEvent(id); 3257d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3258d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DIRTY) 3259d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden && modValues.getAsInteger(Events.DIRTY) == 0) { 3260d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.removeDuplicateEvent(id); 3261d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3262d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3263d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int result = mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 3264d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden new String[] { String.valueOf(id) }); 3265d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (result > 0) { 3266d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden updateEventRawTimesLocked(id, modValues); 3267d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mInstancesHelper.updateInstancesLocked(modValues, id, 3268d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden false /* not a new event */, mDb); 3269d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3270d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // XXX: should we also be doing this when RRULE changes (e.g. instances 3271d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // are introduced or removed?) 3272d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3273d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.STATUS)) { 3274d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If this is a cancellation knock it out 3275d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // of the instances table 3276d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.STATUS) && 3277d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 3278d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String[] args = new String[] {String.valueOf(id)}; 3279d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 3280d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3281d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3282d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // The start time or status of the event changed, so run the 3283d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // event alarm scheduler. 3284d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3285d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "updateInternal() changing event"); 3286d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3287d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3288d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3289d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3290d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(id, callerIsSyncAdapter); 3291d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3292d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3293d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3294d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 3295d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(callerIsSyncAdapter); 3296fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3297fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3298d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3299d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return cursor.getCount(); 3300fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3301fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 33029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 33039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 3304b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 3305ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 33069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 33079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33080739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 33090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_UPDATE, uri, values, callerIsSyncAdapter, match, 33100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 33119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int count = 0; 33139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: remove this restriction 331543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!TextUtils.isEmpty(selection) && match != CALENDAR_ALERTS 3316315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio && match != EVENTS && match != CALENDARS && match != PROVIDER_PROPERTIES) { 3317b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio throw new IllegalArgumentException("WHERE based updates not supported"); 33189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3319fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 33209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 33219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 33229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 33239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff appendAccountToSelection(uri, selection), selectionArgs); 33249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 33269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = appendAccountToSelection(uri, selection); 33272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 3328dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 33299323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3330dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3331dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3332dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 33339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 333543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 33369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 33379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 333843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 333943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 334043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (selection != null) { 334143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio throw new UnsupportedOperationException("Selection not permitted for " 334243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio + uri); 334343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 334443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 334543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 334643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 334743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 334843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 334943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 335043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 33514cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik if (selection != null && TextUtils.equals(selection,"_id=?")) { 33524cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik id = Long.parseLong(selectionArgs[0]); 33534cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik } else if (selection != null && selection.startsWith("_id=")) { 335443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 335543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 335643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 335743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 3358b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 335943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 336043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 336143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 3362c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Calendars.DIRTY, 1); 33632fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 33649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 33659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 33669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 33679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3369b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 3370636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 33719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 33723ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 3373d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 33744067700dbedcf4c8a379c9ecba9b5603972b4607Andy McFadden if (values.containsKey(Calendars.VISIBLE)) { 3375d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 3376d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 3377d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 3378d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 3379420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 3380d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 33813ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 3382dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 33833ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 33843ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 33859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 33869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 33877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 33889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 33899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 3390d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Cursor events = null; 33919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3392d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Grab the full set of columns for each selected event. 3393d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: define a projection with just the data we need (e.g. we don't need to 3394d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // validate the SYNC_* columns) 33959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3396d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3397d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (match == EVENTS_ID) { 3398d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Single event, identified by ID. 3399d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = ContentUris.parseId(uri); 3400d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3401d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden SQL_WHERE_ID, new String[] { String.valueOf(id) }, 3402d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 34039ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } else { 3404d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // One or more events, identified by the selection / selectionArgs. 3405d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3406d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Only the sync adapter can use this URI. 3407d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Invalid URI: " + uri); 34089ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 3409d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3410d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 3411d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden selection, selectionArgs, 3412d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 34139ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 341406c305d35741db303bd3aacd0eab5af8de0ab34eErik 3415d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events.getCount() == 0) { 3416d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.w(TAG, "No events to update: uri=" + uri + " selection=" + selection + 3417d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 3418d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return 0; 3419d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 34203ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 3421d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return handleUpdateEvents(events, values, callerIsSyncAdapter); 3422d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } finally { 3423d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events != null) { 3424d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events.close(); 3425fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 34269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34282fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case ATTENDEES_ID: { 34292fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 34302fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 34312fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 34329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 34339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 34349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 34357e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 34367e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3437b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.ATTENDEES, values, SQL_WHERE_ID, 343883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 34397e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3440b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.ATTENDEES, values, uri, null /* selection */, 34412fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 34427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34442fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 34452fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 34462fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 34472fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 34482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 34492fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 34509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3451b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 3452636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 34539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34542fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 34552fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 34562fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 3457b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 34589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34592fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 34602fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 34612fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 34622fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 34637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 34647e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3465b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = mDb.update(Tables.REMINDERS, values, SQL_WHERE_ID, 346683512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff new String[] {String.valueOf(id)}); 34677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3468b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio count = updateInTable(Tables.REMINDERS, values, uri, null /* selection */, 34692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 34707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 34729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 34739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 34749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 34759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 34769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3477420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */); 34787e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 34799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 34802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: { 34812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (selection != null) { 34822fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff throw new UnsupportedOperationException("Selection not permitted for " + uri); 34832fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 34847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 34857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3486b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.EXTENDED_PROPERTIES, values, SQL_WHERE_ID, 3487636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 34887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 3489b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return updateInTable(Tables.EXTENDED_PROPERTIES, values, uri, 3490b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio null /* selection */, null /* selectionArgs */); 34917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 34929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 349383512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // TODO: replace the SCHEDULE_ALARM private URIs with a 349483512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff // service 349583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM: { 3496420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(false); 349783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 349883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 349983512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 3500420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.scheduleNextAlarm(true); 350183512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 350283512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 35039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3504315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 3505315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (selection == null) { 3506315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection cannot be null for " + uri); 3507315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3508315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 3509315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 3510315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3511315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3512315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 3513315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3514315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 3515315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 3516315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 3517315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3518315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3519315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 3520315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 3521315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3522315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 3523315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 3524b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 3525315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 3526315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 3527f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "home", set the Instances 3528f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the previous 3529f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "auto", set the Instances 3530f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the current 3531f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // device one 3532f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone Instances is set AND if we are in "home" 3533f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone type, then save the timezone Instance into 3534f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // "previous" too 3535315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 3536315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 3537315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 3538315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 3539315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 3540315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 3541315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 3542315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 3543315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 3544315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 3545315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 3546315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3547315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 3548d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 3549315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 3550315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3551d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3552315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3553315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3554315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 3555315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 3556315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 3557315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 3558315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 3559315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3560d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3561315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3562315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3563315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3564315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3565315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 3566315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 3567315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 3568315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 3569315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 3570315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 3571315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 3572315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 3573d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 3574315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 3575315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 3576315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 3577d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 3578315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3579315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3580315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3581315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3582315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 3583315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 3584315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 35859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 35869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 35879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 35899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 35909ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendAccountFromParameterToSelection(String selection, Uri uri) { 3591b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3592b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 3593b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3594b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 3595595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff if (!TextUtils.isEmpty(accountName)) { 35969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 35979ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(Calendars.ACCOUNT_NAME + "=") 35989ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountName)) 35999ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" AND ") 36009ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(Calendars.ACCOUNT_TYPE) 36019ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(" = ") 36029ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert .append(DatabaseUtils.sqlEscapeString(accountType)); 36039ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 3604595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } else { 36059ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 36069ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 36079ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 36089ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 36099ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendLastSyncedColumnToSelection(String selection, Uri uri) { 36109ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (getIsCallerSyncAdapter(uri)) { 36119ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 3612595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 36139ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 3614b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sb.append(CalendarContract.Events.LAST_SYNCED).append(" = 0"); 36159ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 3616595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 3617595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 36189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String appendAccountToSelection(Uri uri, String selection) { 3619b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountName = QueryParameterUtils.getQueryParameter(uri, 3620b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 3621b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio final String accountType = QueryParameterUtils.getQueryParameter(uri, 3622b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 36239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(accountName)) { 3624b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Calendars.ACCOUNT_NAME 3625b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + "=" + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3626b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Calendars.ACCOUNT_TYPE + "=" 36270739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountType)); 36289ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 36290739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 36300739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return selection; 36310739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 36320739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 36330739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 36340739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private String appendSyncAccountToSelection(Uri uri, String selection) { 36350739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountName = QueryParameterUtils.getQueryParameter(uri, 3636b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 36370739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountType = QueryParameterUtils.getQueryParameter(uri, 3638b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 36390739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (!TextUtils.isEmpty(accountName)) { 3640b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik StringBuilder selectionSb = new StringBuilder(CalendarContract.Events.ACCOUNT_NAME + "=" 36410739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + DatabaseUtils.sqlEscapeString(accountName) + " AND " 3642b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events.ACCOUNT_TYPE + "=" 36439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + DatabaseUtils.sqlEscapeString(accountType)); 36449ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(selectionSb, selection); 36459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 36469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 36479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 36509ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendSelection(StringBuilder sb, String selection) { 36519ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!TextUtils.isEmpty(selection)) { 36529ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(" AND ("); 36539ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(selection); 36549ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(')'); 36559ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 36569ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return sb.toString(); 36579ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 36589ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 36590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik /** 36600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * Verifies that the operation is allowed and throws an exception if it 36610739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * isn't. This defines the limits of a sync adapter call vs an app call. 3662c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik * 36630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param type The type of call, {@link #TRANSACTION_QUERY}, 36640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_INSERT}, {@link #TRANSACTION_UPDATE}, or 36650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_DELETE} 36660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param uri 36670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param values 36680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param isSyncAdapter 36690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik */ 36700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyTransactionAllowed(int type, Uri uri, ContentValues values, 36710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik boolean isSyncAdapter, int uriMatch, String selection, String[] selectionArgs) { 36720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (type) { 36730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_QUERY: 36740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 36750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_INSERT: 36760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 36770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException( 36780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Inserting into instances not supported"); 36790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 3680c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 3681c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 36820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 36830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 36840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 36850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 36860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 36870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 36880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 36890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 36900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_UPDATE: 36910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 36920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Updating instances not supported"); 36930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 3694c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 3695c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 36960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 36970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 36980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 36990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 37000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 37010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 37020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 37040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_DELETE: 37050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 37060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Deleting instances not supported"); 37070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37080739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 37090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 37100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 37110739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37120739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 37130739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37140739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37150739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 37160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyHasAccount(Uri uri, String selection, String[] selectionArgs) { 3717c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = QueryParameterUtils.getQueryParameter(uri, Calendars.ACCOUNT_NAME); 37180739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String accountType = QueryParameterUtils.getQueryParameter(uri, 3719c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 37200739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 37210739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (selection != null && selection.startsWith(ACCOUNT_SELECTION_PREFIX)) { 37220739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountName = selectionArgs[0]; 37230739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountType = selectionArgs[1]; 37240739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37250739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37260739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 37270739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException( 37280739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Sync adapters must specify an account and account type: " + uri); 37290739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37300739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37310739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 3732c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private void verifyColumns(ContentValues values, int uriMatch) { 3733c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 3734c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik return; 3735c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3736c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String[] columns; 3737c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik switch (uriMatch) { 3738c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 3739c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 3740c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 3741c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 3742c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = Events.PROVIDER_WRITABLE_COLUMNS; 3743c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3744c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik default: 3745c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = PROVIDER_WRITABLE_DEFAULT_COLUMNS; 3746c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3747c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3748c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 3749c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik for (int i = 0; i < columns.length; i++) { 3750c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values.containsKey(columns[i])) { 3751c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik throw new IllegalArgumentException("Only the provider may write to " + columns[i]); 3752c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3753c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3754c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 3755c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 37560739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyNoSyncColumns(ContentValues values, int uriMatch) { 3757c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 37580739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 37590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String[] syncColumns; 37610739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (uriMatch) { 37620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS: 37630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS_ID: 37640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES: 37650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES_ID: 3766c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Calendars.SYNC_WRITABLE_COLUMNS; 3767c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 3768c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 3769c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 3770c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 3771c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 3772c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Events.SYNC_WRITABLE_COLUMNS; 37730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 37740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik default: 37750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik syncColumns = SYNC_WRITABLE_DEFAULT_COLUMNS; 37760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 37770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 37780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37790739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik for (int i = 0; i < syncColumns.length; i++) { 37800739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (values.containsKey(syncColumns[i])) { 37810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException("Only sync adapters may write to " 37820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + syncColumns[i]); 37830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 37860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 37879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 37889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 37899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 37909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 3791c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik new String[] {Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, 3792fa332ecedc0c340109811552407142f6e4f600b2RoboErik Calendars.CAL_SYNC1, Calendars.SYNC_EVENTS}, 37939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 37949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 37959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 37969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 37979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 37989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 37999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 3800ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 38019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 3802ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 3803ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 3804ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 3805ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 3806ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 3807ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 3808ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 38099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 38109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 38119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38149535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 38159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 3816f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3817f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 3818f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 3819f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 38209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 38219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38239535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 38249535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 38259535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 38269535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 38279535627bf6295cd94447beb83e1aac41f50c3600Erik } 38289535627bf6295cd94447beb83e1aac41f50c3600Erik 38299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 38309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 38319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 38329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 38349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 38359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 38369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 38379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 38389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3839a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3840a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3841a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3842a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 3843dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 38449ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 3845a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3846dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 3847dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 3848dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 3849a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3850a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3851a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3852a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 3853a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 3854a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 3855a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 3856a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 3857a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 3858a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 3859a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 38609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param eventId the ID of the event that changed, or -1 for no specific event 38619ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 3862a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3863dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 3864dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 3865a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 3866a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 3867a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 3868a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 3869a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 3870dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 3871dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 3872dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 3873dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 3874dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 3875dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 3876dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 3877dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 3878dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 3879dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 3880dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 3881dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 3882dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 3883dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 3884dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 3885dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 3886dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 3887a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 3888dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 3889a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3890a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 3891a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 3892a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 3893a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 38949ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #sendUpdateNotification(boolean)} instead. 3895a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 38969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @see #sendUpdateNotification(boolean) 3897a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 3898a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 3899a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 3900b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.CONTENT_URI); 3901f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 3902f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 3903f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 3904e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 3905a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 3906a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 39070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_QUERY = 0; 39080739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_INSERT = 1; 39090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_UPDATE = 2; 39100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_DELETE = 3; 39110739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 39120739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:off 39130739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final String[] SYNC_WRITABLE_DEFAULT_COLUMNS = new String[] { 3914b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars.DIRTY, 3915b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars._SYNC_ID 39160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik }; 3917c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String[] PROVIDER_WRITABLE_DEFAULT_COLUMNS = new String[] { 3918c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik }; 39190739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:on 39200739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 39219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 39229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 39239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 39242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS = 4; 39252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS_ID = 5; 39262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES = 6; 39272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES_ID = 7; 39282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS = 8; 39292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS_ID = 9; 39302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES = 10; 39312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES_ID = 11; 39322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS = 12; 39332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_ID = 13; 39342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_BY_INSTANCE = 14; 39352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_BY_DAY = 15; 39362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE = 16; 39372ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE_ID = 17; 39382ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES = 18; 39392ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES_ID = 19; 39402ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_DAYS = 20; 39412ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM = 21; 39422ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM_REMOVE = 22; 39432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int TIME = 23; 39442ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES = 24; 39452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES_ID = 25; 39462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH = 26; 39472ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH_BY_DAY = 27; 39482ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int PROVIDER_PROPERTIES = 28; 3949bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID = 29; 3950bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID2 = 30; 39513b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static final int EMMA = 31; 39529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 39549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 3955f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final HashMap<String, String> sEventsProjectionMap; 395619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 39579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 39589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 39599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 3960315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 396139c65e5716e21e863d8de587d139dae85f99422fFred Quintana private static final HashMap<String, String> sCountProjectionMap; 39629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 3964b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/when/*/*", INSTANCES); 3965b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 3966b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 3967b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/searchbyday/*/*/*", 396881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 3969b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 3970b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events", EVENTS); 3971b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events/#", EVENTS_ID); 3972b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities", EVENT_ENTITIES); 3973b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 3974b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars", CALENDARS); 3975b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars/#", CALENDARS_ID); 3976b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 3977b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 3978b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees", ATTENDEES); 3979b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees/#", ATTENDEES_ID); 3980b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders", REMINDERS); 3981b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders/#", REMINDERS_ID); 3982b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 3983b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties/#", 3984b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik EXTENDED_PROPERTIES_ID); 3985b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 3986b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 3987b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/by_instance", 3988b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 3989b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate", SYNCSTATE); 3990b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 3991b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, CalendarAlarmManager.SCHEDULE_ALARM_PATH, 3992420b7fb569773ae573fbe90c3a9c522d4c368863Erik SCHEDULE_ALARM); 3993b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, 3994b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 3995b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time/#", TIME); 3996b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time", TIME); 3997b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "properties", PROVIDER_PROPERTIES); 3998b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID); 3999b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2); 40003b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden sUriMatcher.addURI(CalendarContract.AUTHORITY, "emma", EMMA); 40019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 400239c65e5716e21e863d8de587d139dae85f99422fFred Quintana /** Contains just BaseColumns._COUNT */ 400339c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap = new HashMap<String, String>(); 400439c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)"); 400539c65e5716e21e863d8de587d139dae85f99422fFred Quintana 40069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 40079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 400802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME); 400902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_TYPE, Events.ACCOUNT_TYPE); 4010c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.TITLE, Events.TITLE); 4011c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4012c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4013c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.STATUS, Events.STATUS); 401402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4015c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4016c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART); 4017c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTEND, Events.DTEND); 4018c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4019c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4020c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DURATION, Events.DURATION); 4021c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4022c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4023c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4024c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4025c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, Events.HAS_EXTENDED_PROPERTIES); 4026c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RRULE, Events.RRULE); 4027c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RDATE, Events.RDATE); 4028c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXRULE, Events.EXRULE); 4029c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXDATE, Events.EXDATE); 4030c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 403134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4032c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, Events.ORIGINAL_INSTANCE_TIME); 4033c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4034c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4035c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4036c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4037c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, Events.GUESTS_CAN_INVITE_OTHERS); 4038c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4039c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4040c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4041c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DELETED, Events.DELETED); 404202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 40439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4044e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 40451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 40461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 40471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 40489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 4049c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 405002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL); 4051c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 405202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 4053c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 405402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 405502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 405602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 405702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND); 405802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 40599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4060982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 4061e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 4062e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 4063982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4064e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4065982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 4066c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events._ID, Events._ID); 406702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 406802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 406902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 407002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 407102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 407202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 40739ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 407402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 407502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 407602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 407702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 407802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 407902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 408002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 408102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 408202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 408302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 408402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 408502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 408602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 4087c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY); 40889ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 40899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 409046f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 4091c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.TITLE, Events.TITLE); 4092c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4093c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4094c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.STATUS, Events.STATUS); 409502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4096c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4097c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTSTART, Events.DTSTART); 4098c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTEND, Events.DTEND); 4099c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4100c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4101c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DURATION, Events.DURATION); 4102c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4103c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4104c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4105c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4106c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, 4107c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.HAS_EXTENDED_PROPERTIES); 4108c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RRULE, Events.RRULE); 4109c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RDATE, Events.RDATE); 4110c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXRULE, Events.EXRULE); 4111c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXDATE, Events.EXDATE); 4112c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 411334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4114c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, 4115c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_INSTANCE_TIME); 4116c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4117c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4118c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4119c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4120c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, 4121c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.GUESTS_CAN_INVITE_OTHERS); 4122c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4123c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4124c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4125c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DELETED, Events.DELETED); 412619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 412719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 412802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 412902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 413002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 413102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 413202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 413302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 41349ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 413502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 413602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 413702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 4138470aa5bc291ca33d51dda356f38ac2954026da9aAlon Albert sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY); 41399ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 4140fa332ecedc0c340109811552407142f6e4f600b2RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 414102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 414202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 414302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 414402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 414502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 414602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 414702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 414802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 414902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 415019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 41519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 41521b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 41539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 41549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 41559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 41569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 41579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 41589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 41599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 41609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 41619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 41639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 41649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 41659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 41669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 41679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 41689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 41699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 417002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 417102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 41729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 41749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 41759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 41769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 41779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 4178361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 4179361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 41809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 41819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 41829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 41839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 41849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 41859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 41869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 41879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 41889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4189315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4190315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4191315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4192315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4193315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 41949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 419664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 41979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 419864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * This is called by AccountManager when the set of accounts is updated. 419964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * <p> 420064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * We are overriding this since we need to delete from the 42019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 42027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 42037e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 420464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * 420564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * @param accounts The list of currently active accounts. 42069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4207f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik @Override 42089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 420964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Thread thread = new AccountsUpdatedThread(accounts); 421064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden thread.start(); 421164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 421264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 421364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private class AccountsUpdatedThread extends Thread { 421464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private Account[] mAccounts; 421564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 421664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountsUpdatedThread(Account[] accounts) { 421764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden mAccounts = accounts; 421864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 421964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 422064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden @Override 422164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden public void run() { 422264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // The process could be killed while the thread runs. Right now that isn't a problem, 422364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // because we'll just call removeStaleAccounts() again when the provider restarts, but 422464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // if we want to do additional actions we may need to use a service (e.g. start 422564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // EmptyService in onAccountsUpdated() and stop it when we finish here). 422664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 422764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 422864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(mAccounts); 422964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 423064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 423164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 423264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden /** 423364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * Makes sure there are no entries for accounts that no longer exist. 423464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden */ 423564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void removeStaleAccounts(Account[] accounts) { 4236ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4237ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 4238ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 4239ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4240ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4241ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 42429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 424346f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>(); 424446f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 42459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 42469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 42479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountHasCalendar.put(account, false); 42489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 42509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 42529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 42539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4254b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio for (String table : new String[]{Tables.CALENDARS}) { 4255ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 42569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 42577cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio Cursor c = mDb.rawQuery("SELECT DISTINCT " + 42582ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_NAME + 42597cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 42602ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + 42617cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 42629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 42634cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // ACCOUNT_TYPE_LOCAL is to store calendars not associated 42644cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // with a system account. Typically, a calendar must be 42654cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // associated with an account on the device or it will be 42664cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // deleted. 4267b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (c.getString(0) != null 4268b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && c.getString(1) != null 4269b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && !TextUtils.equals(c.getString(1), 4270b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.ACCOUNT_TYPE_LOCAL)) { 42719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 42729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 42739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 42749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 42789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 4281f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 4282f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 4283f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 42849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 4285b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 42869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 42889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 42899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 42909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 42919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42923ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 42933ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 4294dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 42959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4297636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 4298636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 4299636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 4300636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 4301636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 4302636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 4303636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 4304636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 4305636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 4306636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 4307636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 4308636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 4309636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 4310636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 4311636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 4312636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 4313636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 4314636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 4315636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 43169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 4317