19f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/* 29f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 39f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Copyright 2006, The Android Open Source Project 49f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 59f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Licensed under the Apache License, Version 2.0 (the "License"); 69f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** you may not use this file except in compliance with the License. 79f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** You may obtain a copy of the License at 89f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 99f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** http://www.apache.org/licenses/LICENSE-2.0 109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** 119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** Unless required by applicable law or agreed to in writing, software 129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** distributed under the License is distributed on an "AS IS" BASIS, 139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** See the License for the specific language governing permissions and 149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff** limitations under the License. 169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff*/ 179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpackage com.android.providers.calendar; 199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.Account; 219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.AccountManager; 229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.accounts.OnAccountsUpdateListener; 23043587d3ef1a9cc156a6819fdcb7ef5b2aa81ed4Dianne Hackbornimport android.app.AppOpsManager; 249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.BroadcastReceiver; 259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentResolver; 269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentUris; 279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.ContentValues; 289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Context; 299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.Intent; 309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.IntentFilter; 319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.content.UriMatcher; 327a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albertimport android.content.pm.PackageManager; 339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.Cursor; 349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.DatabaseUtils; 359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.SQLException; 369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteDatabase; 379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.database.sqlite.SQLiteQueryBuilder; 389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.net.Uri; 397a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albertimport android.os.Binder; 40a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Handler; 41a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tangimport android.os.Message; 429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.os.Process; 439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.provider.BaseColumns; 44b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract; 45b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Attendees; 46b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.CalendarAlerts; 47b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Calendars; 482f251c778c06d21ed7693a70f4a1268ff929242eRoboErikimport android.provider.CalendarContract.Colors; 49b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Events; 50b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Instances; 51b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.Reminders; 52b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErikimport android.provider.CalendarContract.SyncState; 539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.text.TextUtils; 541edaf77a7ef2fbad6b6116dd75591e0aeaff3a16Ken Shirriffimport android.text.format.DateUtils; 55192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blankimport android.text.format.Time; 569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.Log; 579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport android.util.TimeFormatException; 58ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglioimport android.util.TimeUtils; 599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 605cd969369a0e025bad07ad32bda9c8c4f0630457Michael Chanimport com.android.calendarcommon2.DateException; 615cd969369a0e025bad07ad32bda9c8c4f0630457Michael Chanimport com.android.calendarcommon2.Duration; 625cd969369a0e025bad07ad32bda9c8c4f0630457Michael Chanimport com.android.calendarcommon2.EventRecurrence; 635cd969369a0e025bad07ad32bda9c8c4f0630457Michael Chanimport com.android.calendarcommon2.RecurrenceProcessor; 645cd969369a0e025bad07ad32bda9c8c4f0630457Michael Chanimport com.android.calendarcommon2.RecurrenceSet; 658d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.providers.calendar.CalendarDatabaseHelper.Tables; 668d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.android.providers.calendar.CalendarDatabaseHelper.Views; 678d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.google.android.collect.Sets; 688d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albertimport com.google.common.annotations.VisibleForTesting; 698d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 703b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.io.File; 712ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErikimport java.lang.reflect.Array; 723b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFaddenimport java.lang.reflect.Method; 739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.ArrayList; 74ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglioimport java.util.Arrays; 759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashMap; 769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.HashSet; 771c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFaddenimport java.util.Iterator; 78dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.List; 79bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFaddenimport java.util.Set; 809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffimport java.util.TimeZone; 81dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tangimport java.util.regex.Matcher; 8281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tangimport java.util.regex.Pattern; 839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff/** 859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendar content provider. The contract between this provider and applications 86b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik * is defined in {@link android.provider.CalendarContract}. 879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriffpublic class CalendarProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener { 899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 918bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static final String TAG = "CalendarProvider2"; 92350bdf916ba93f0318a5c3b0cbd2c9794c748c80Tony Mak // Turn on for b/22449592 93350bdf916ba93f0318a5c3b0cbd2c9794c748c80Tony Mak static final boolean DEBUG_INSTANCES = Log.isLoggable(TAG, Log.DEBUG); 949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 957be45683e367bd6897daf6444b03be938f8f5eaaErik private static final String TIMEZONE_GMT = "GMT"; 96c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String ACCOUNT_SELECTION_PREFIX = Calendars.ACCOUNT_NAME + "=? AND " 97c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik + Calendars.ACCOUNT_TYPE + "=?"; 987be45683e367bd6897daf6444b03be938f8f5eaaErik 99f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final boolean PROFILE = false; 1009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final boolean MULTIPLE_ATTENDEES_PER_EVENT = true; 1018f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 1021ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff private static final String[] ID_ONLY_PROJECTION = 1031ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff new String[] {Events._ID}; 1049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] EVENTS_PROJECTION = new String[] { 1069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events._SYNC_ID, 1079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RRULE, 1089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Events.RDATE, 109b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Events.ORIGINAL_ID, 110c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_SYNC_ID, 1119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 1129ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 1139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_SYNC_ID_INDEX = 0; 1147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RRULE_INDEX = 1; 1157e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENTS_RDATE_INDEX = 2; 116b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_ID_INDEX = 3; 117b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4; 1187e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 1192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] COLORS_PROJECTION = new String[] { 1202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_NAME, 1212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.ACCOUNT_TYPE, 1222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR_TYPE, 123387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik Colors.COLOR_KEY, 1242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.COLOR, 1252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_NAME_INDEX = 0; 1272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_ACCOUNT_TYPE_INDEX = 1; 1282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_TYPE_INDEX = 2; 1292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX_INDEX = 3; 1302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS_COLOR_INDEX = 4; 1312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1324755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private static final String COLOR_FULL_SELECTION = Colors.ACCOUNT_NAME + "=? AND " 1334755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + Colors.ACCOUNT_TYPE + "=? AND " + Colors.COLOR_TYPE + "=? AND " + Colors.COLOR_KEY 1344755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + "=?"; 1354755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan 1362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_NAME = Calendars.ACCOUNT_NAME; 1372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String GENERIC_ACCOUNT_TYPE = Calendars.ACCOUNT_TYPE; 1382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String[] ACCOUNT_PROJECTION = new String[] { 1392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_NAME, 1402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik GENERIC_ACCOUNT_TYPE, 1412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 1422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_NAME_INDEX = 0; 1432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int ACCOUNT_TYPE_INDEX = 1; 1442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // many tables have _id and event_id; pick a representative version to use as our generic 1461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_ID = Attendees._ID; 1471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private static final String GENERIC_EVENT_ID = Attendees.EVENT_ID; 1481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 1497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final String[] ID_PROJECTION = new String[] { 1501c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_ID, 1511c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden GENERIC_EVENT_ID, 1527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff }; 1537e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int ID_INDEX = 0; 1547e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff private static final int EVENT_ID_INDEX = 1; 1559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 157646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Projection to query for correcting times in allDay events. 158646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 159646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final String[] ALLDAY_TIME_PROJECTION = new String[] { 160646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events._ID, 161646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTSTART, 162646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DTEND, 163646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik Events.DURATION 164646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik }; 165646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_ID_INDEX = 0; 166646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTSTART_INDEX = 1; 167646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DTEND_INDEX = 2; 168646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int ALLDAY_DURATION_INDEX = 3; 169646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 170646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik private static final int DAY_IN_SECONDS = 24 * 60 * 60; 171646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 172646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * The cached copy of the CalendarMetaData database table. 1749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Make this "package private" instead of "private" so that test code 1759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * can access it. 1769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData mMetaData; 178ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio CalendarCache mCalendarCache; 1799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private CalendarDatabaseHelper mDbHelper; 181f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik private CalendarInstancesHelper mInstancesHelper; 1829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1833443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private static final String SQL_SELECT_EVENTSRAWTIMES = "SELECT " + 184b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + ", " + 185b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTSTART_2445 + ", " + 186b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.DTEND_2445 + ", " + 1873443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Events.EVENT_TIMEZONE + 1883443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " FROM " + 189b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS_RAW_TIMES + ", " + 190b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.EVENTS + 1913443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio " WHERE " + 192b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID; 193b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 1947a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert private static final String SQL_UPDATE_EVENT_SET_DIRTY_AND_MUTATORS = "UPDATE " + 1957a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert Tables.EVENTS + " SET " + 1967a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert Events.DIRTY + "=1," + 1977a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert Events.MUTATORS + "=? " + 1987a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert " WHERE " + Events._ID + "=?"; 1997a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert 2007a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert private static final String SQL_QUERY_EVENT_MUTATORS = "SELECT " + Events.MUTATORS + 2017a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert " FROM " + Tables.EVENTS + 202b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " WHERE " + Events._ID + "=?"; 203b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_WHERE_CALENDAR_COLOR = Calendars.ACCOUNT_NAME + "=? AND " 205387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik + Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.CALENDAR_COLOR_KEY + "=?"; 2062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 207f4b1756f88c34efefb33b4103230ee334d9c9262Sara Ting private static final String SQL_WHERE_EVENT_COLOR = "calendar_id in (SELECT _id from " 20810651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan + Tables.CALENDARS + " WHERE " + Events.ACCOUNT_NAME + "=? AND " + Events.ACCOUNT_TYPE 20910651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan + "=?) AND " + Events.EVENT_COLOR_KEY + "=?"; 2102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 21124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden protected static final String SQL_WHERE_ID = GENERIC_ID + "=?"; 21224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private static final String SQL_WHERE_EVENT_ID = GENERIC_EVENT_ID + "=?"; 2134d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?"; 2144d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden private static final String SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID = Events.ORIGINAL_ID + 2154d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden "=? AND " + Events._SYNC_ID + " IS NULL"; 216ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 217ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan private static final String SQL_WHERE_ATTENDEE_BASE = 218ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID 219ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan + " AND " + 220ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 221ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan 222b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_ATTENDEES_ID = 223ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.ATTENDEES + "." + Attendees._ID + "=? AND " + SQL_WHERE_ATTENDEE_BASE; 224b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 225b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_REMINDERS_ID = 226b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.REMINDERS + "." + Reminders._ID + "=? AND " + 227ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events._ID + "=" + Tables.REMINDERS + "." + Reminders.EVENT_ID + 228ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan " AND " + 229ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan Tables.EVENTS + "." + Events.CALENDAR_ID + "=" + Tables.CALENDARS + "." + Calendars._ID; 230b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 231b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT = 2322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 233b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID; 234b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 235b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_CALENDAR_ALERT_ID = 2362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Views.EVENTS + "." + Events._ID + "=" + 237b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts.EVENT_ID + 238b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio " AND " + 239b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Tables.CALENDAR_ALERTS + "." + CalendarAlerts._ID + "=?"; 240b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 241b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_EXTENDED_PROPERTIES_ID = 242b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik Tables.EXTENDED_PROPERTIES + "." + CalendarContract.ExtendedProperties._ID + "=?"; 243b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 244b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_DELETE_FROM_CALENDARS = "DELETE FROM " + Tables.CALENDARS + 2452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik " WHERE " + Calendars.ACCOUNT_NAME + "=? AND " + 2462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + "=?"; 247b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio 2482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final String SQL_DELETE_FROM_COLORS = "DELETE FROM " + Tables.COLORS + " WHERE " 2492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?"; 2502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 251fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private static final String SQL_SELECT_COUNT_FOR_SYNC_ID = 252fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio "SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?"; 253fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 2549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Make sure we load at least two months worth of data. 2559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Client apps can load more data in a background thread. 2569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final long MINIMUM_EXPANSION_SPAN = 2579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2L * 31 * 24 * 60 * 60 * 1000; 2589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final String[] sCalendarsIdProjection = new String[] { Calendars._ID }; 2609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int CALENDARS_INDEX_ID = 0; 2619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 26281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String INSTANCE_QUERY_TABLES = 26381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 26481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 26581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + 26681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 267b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 26881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 269b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 27081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 27118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String INSTANCE_SEARCH_QUERY_TABLES = "(" + 27218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.INSTANCES + " INNER JOIN " + 27318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Views.EVENTS + " AS " + 27418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + 27518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.INSTANCES + "." 276b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Instances.EVENT_ID + "=" + 27718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 278b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")" + ") LEFT OUTER JOIN " + 27918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.ATTENDEES + 28018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang " ON (" + CalendarDatabaseHelper.Tables.ATTENDEES + "." 281b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Attendees.EVENT_ID + "=" + 28218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang CalendarDatabaseHelper.Tables.EVENTS + "." 283b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik + CalendarContract.Events._ID + ")"; 28418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 285b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN_DAY = 286b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.START_DAY + "<=? AND " + 287b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END_DAY + ">=?"; 28881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 289b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio private static final String SQL_WHERE_INSTANCES_BETWEEN = 290b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.BEGIN + "<=? AND " + 291b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Instances.END + ">=?"; 2929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_DAY = 0; 2949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_DAY = 1; 2959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_START_MINUTE = 2; 2969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_END_MINUTE = 3; 2979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES_INDEX_ALL_DAY = 4; 2989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 3002ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * The sort order is: events with an earlier start time occur first and if 3012ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the start times are the same, then events with a later end time occur 3022ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * first. The later end time is ordered first so that long-running events in 3032ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * the calendar views appear first. If the start and end times of two events 3042ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * are the same then we sort alphabetically on the title. This isn't 3052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * required for correctness, it just adds a nice touch. 3062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 3072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC"; 3082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 3092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 3102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * A regex for describing how we split search queries into tokens. Keeps 3112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * quoted phrases as one token. "one \"two three\"" ==> ["one" "two three"] 312dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 313dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_TOKEN_PATTERN = 314dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("[^\\s\"'.?!,]+|" // first part matches unquoted words 315dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang + "\"([^\"]*)\""); // second part matches quoted phrases 316dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 317dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A special character that was use to escape potentially problematic 318dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * characters in search queries. 319dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * 320dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Note: do not use backslash for this, as it interferes with the regex 321dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * escaping mechanism. 32281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 323dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final String SEARCH_ESCAPE_CHAR = "#"; 324dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 325dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 326dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * A regex for matching any characters in an incoming search query that we 327dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * need to escape with {@link #SEARCH_ESCAPE_CHAR}, including the escape 328dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * character itself. 329dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 330dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang private static final Pattern SEARCH_ESCAPE_PATTERN = 331dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Pattern.compile("([%_" + SEARCH_ESCAPE_CHAR + "])"); 33281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 33318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 33418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee e-mails when grouping 33518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 33618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 33718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_EMAIL_CONCAT = 338b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_EMAIL + ")"; 33918f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 34018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang /** 34118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * Alias used for aggregate concatenation of attendee names when grouping 34218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang * attendees by instance. 34318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang */ 34418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang private static final String ATTENDEES_NAME_CONCAT = 345b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik "group_concat(" + CalendarContract.Attendees.ATTENDEE_NAME + ")"; 34618f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang 34781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private static final String[] SEARCH_COLUMNS = new String[] { 348b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.TITLE, 349b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.DESCRIPTION, 350b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Events.EVENT_LOCATION, 35118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_EMAIL_CONCAT, 35218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang ATTENDEES_NAME_CONCAT 35381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang }; 35481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 355a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 356a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Arbitrary integer that we assign to the messages that we send to this 357a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * thread's handler, indicating that these are requests to send an update 358a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * notification intent. 359a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 360a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final int UPDATE_BROADCAST_MSG = 1; 361a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 362a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 363a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Any requests to send a PROVIDER_CHANGED intent will be collapsed over 364a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * this window, to prevent spamming too many intents at once. 365a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 366a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private static final long UPDATE_BROADCAST_TIMEOUT_MILLIS = 367dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang DateUtils.SECOND_IN_MILLIS; 368dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 369dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private static final long SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS = 370dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 30 * DateUtils.SECOND_IN_MILLIS; 371dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang 3728d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private static final HashSet<String> ALLOWED_URI_PARAMETERS = Sets.newHashSet( 3738d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.CALLER_IS_SYNCADAPTER, 3748d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.EventsEntity.ACCOUNT_NAME, 3758d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert CalendarContract.EventsEntity.ACCOUNT_TYPE); 3768d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 377bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Set of columns allowed to be altered when creating an exception to a recurring event. */ 378bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final HashSet<String> ALLOWED_IN_EXCEPTION = new HashSet<String>(); 379bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden static { 380bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // _id, _sync_account, _sync_account_type, dirty, _sync_mark, calendar_id 381bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events._SYNC_ID); 382bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA1); 383bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA7); 38402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA3); 385bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.TITLE); 386bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION); 387bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION); 3882f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR); 389387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR_KEY); 390bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.STATUS); 391c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS); 39202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6); 393bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DTSTART); 394c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // dtend -- set from duration as part of creating the exception 395bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_TIMEZONE); 396bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EVENT_END_TIMEZONE); 397bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.DURATION); 398bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ALL_DAY); 399bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ACCESS_LEVEL); 400bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.AVAILABILITY); 401bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ALARM); 402bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_EXTENDED_PROPERTIES); 403bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RRULE); 404bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.RDATE); 405bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXRULE); 406bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.EXDATE); 407bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_SYNC_ID); 408bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORIGINAL_INSTANCE_TIME); 409bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // originalAllDay, lastDate 410bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.HAS_ATTENDEE_DATA); 411bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_MODIFY); 412bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_INVITE_OTHERS); 413bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.GUESTS_CAN_SEE_GUESTS); 414bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ALLOWED_IN_EXCEPTION.add(Events.ORGANIZER); 415c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan ALLOWED_IN_EXCEPTION.add(Events.CUSTOM_APP_PACKAGE); 416c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan ALLOWED_IN_EXCEPTION.add(Events.CUSTOM_APP_URI); 417501e60bcb1b519d80723f7b64ba60bd079b8ec8dSara Ting ALLOWED_IN_EXCEPTION.add(Events.UID_2445); 418bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // deleted, original_id, alerts 419bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 420bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 421bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** Don't clone these from the base event into the exception event. */ 422bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final String[] DONT_CLONE_INTO_EXCEPTION = { 423bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events._SYNC_ID, 424bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA1, 42502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA2, 42602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA3, 42702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA4, 42802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA5, 42902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA6, 430bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.SYNC_DATA7, 43102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik Events.SYNC_DATA8, 432c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA9, 433c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Events.SYNC_DATA10, 434bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden }; 435bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 436bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** set to 'true' to enable debug logging for recurrence exception code */ 437bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final boolean DEBUG_EXCEPTION = false; 438bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 439dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private Context mContext; 440e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio private ContentResolver mContentResolver; 441e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 4428bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio private static CalendarProvider2 mInstance; 4438bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 444420b7fb569773ae573fbe90c3a9c522d4c368863Erik @VisibleForTesting 445420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected CalendarAlarmManager mCalendarAlarm; 446a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 447a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private final Handler mBroadcastHandler = new Handler() { 448a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang @Override 449a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang public void handleMessage(Message msg) { 450dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang Context context = CalendarProvider2.this.mContext; 451a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (msg.what == UPDATE_BROADCAST_MSG) { 452a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Broadcast a provider changed intent 453a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang doSendUpdateNotification(); 454dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 455dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 456dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 457dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 458dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 459a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang context.stopService(new Intent(context, EmptyService.class)); 460a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 461a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 462a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang }; 4639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 4659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Listens for timezone changes and disk-no-longer-full events 4669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 4689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onReceive(Context context, Intent intent) { 4709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String action = intent.getAction(); 4719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 4729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "onReceive() " + action); 4739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 4759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 476ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 4779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { 4789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Try to clean up if things were screwy due to a full disk 4799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateTimezoneDependentFields(); 480ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 4819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { 482ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 4839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff }; 4869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* Visible for testing */ 4889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 4899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected CalendarDatabaseHelper getDatabaseHelper(final Context context) { 4909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return CalendarDatabaseHelper.getInstance(context); 4919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4938bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio protected static CalendarProvider2 getInstance() { 4948bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio return mInstance; 4958bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 4968bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 497e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio @Override 498e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio public void shutdown() { 499e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio if (mDbHelper != null) { 500e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper.close(); 501e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDbHelper = null; 502e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mDb = null; 503e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 5048bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio } 5058bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 5079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public boolean onCreate() { 5089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff super.onCreate(); 509043587d3ef1a9cc156a6819fdcb7ef5b2aa81ed4Dianne Hackborn setAppOps(AppOpsManager.OP_READ_CALENDAR, AppOpsManager.OP_WRITE_CALENDAR); 510ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 511ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return initialize(); 512ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (RuntimeException e) { 513f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 514f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot start provider", e); 515f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 516ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return false; 517ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 518ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 5199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 520ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private boolean initialize() { 5218bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio mInstance = this; 5228bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 523dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext = getContext(); 524e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContentResolver = mContext.getContentResolver(); 525e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 526ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDbHelper = (CalendarDatabaseHelper)getDatabaseHelper(); 527ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mDb = mDbHelper.getWritableDatabase(); 5289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5294caf8d015918f619a67d321a152f150a01022717Andy McFadden mMetaData = new MetaData(mDbHelper); 5304caf8d015918f619a67d321a152f150a01022717Andy McFadden mInstancesHelper = new CalendarInstancesHelper(mDbHelper, mMetaData); 5314caf8d015918f619a67d321a152f150a01022717Andy McFadden 5329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Register for Intent broadcasts 5339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff IntentFilter filter = new IntentFilter(); 5349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 5369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); 5379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff filter.addAction(Intent.ACTION_TIME_CHANGED); 5389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't ever unregister this because this thread always wants 5409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // to receive notifications, even in the background. And if this 5419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // thread is killed then the whole process will be killed and the 5429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // memory resources will be reclaimed. 543e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.registerReceiver(mIntentReceiver, filter); 5449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 545ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache = new CalendarCache(mDbHelper); 546ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 547420b7fb569773ae573fbe90c3a9c522d4c368863Erik // This is pulled out for testing 548420b7fb569773ae573fbe90c3a9c522d4c368863Erik initCalendarAlarm(); 549e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 550e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio postInitialize(); 5518bb142159463f654ef07e20a341fcb527f0109f2Fabrice Di Meglio 5529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return true; 5539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 5549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 555420b7fb569773ae573fbe90c3a9c522d4c368863Erik protected void initCalendarAlarm() { 556420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = getOrCreateCalendarAlarmManager(); 557e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 558e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 559e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio synchronized CalendarAlarmManager getOrCreateCalendarAlarmManager() { 560420b7fb569773ae573fbe90c3a9c522d4c368863Erik if (mCalendarAlarm == null) { 561420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm = new CalendarAlarmManager(mContext); 56298e01573880a2f1b1547d1e2e9a1c5c7d5e09043Alon Albert Log.i(TAG, "Created " + mCalendarAlarm + "(" + this + ")"); 563e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 564420b7fb569773ae573fbe90c3a9c522d4c368863Erik return mCalendarAlarm; 565e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio } 566e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio 567ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio protected void postInitialize() { 568ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Thread thread = new PostInitializeThread(); 569ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio thread.start(); 570ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 571ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 572ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio private class PostInitializeThread extends Thread { 573ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio @Override 574ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio public void run() { 575ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 576ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 577ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio verifyAccounts(); 578ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 579c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan try { 580c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan doUpdateTimezoneDependentFields(); 581c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan } catch (IllegalStateException e) { 582c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // Added this because tests would fail if the provider is 583c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // closed by the time this is executed 584c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan 585c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan // Nothing actionable here anyways. 586c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan } 587ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 588ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 589ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio 59064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void verifyAccounts() { 59164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false); 59264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(AccountManager.get(getContext()).getAccounts()); 59364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 59464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 59564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 5969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 5979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This creates a background thread to check the timezone and update 5989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the timezone dependent fields in the Instances table if the timezone 599315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * has changed. 6009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 6019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected void updateTimezoneDependentFields() { 6029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Thread thread = new TimezoneCheckerThread(); 6039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff thread.start(); 6049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private class TimezoneCheckerThread extends Thread { 6079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 6089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void run() { 6099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 610ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio doUpdateTimezoneDependentFields(); 6119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 6139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 6149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 615315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * Check if we are in the same time zone 616315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio */ 617315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isLocalSameAsInstancesTimezone() { 618315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 619315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return TextUtils.equals(mCalendarCache.readTimezoneInstances(), localTimezone); 620315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 621315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 622315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio /** 6239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * This method runs in a background thread. If the timezone has changed 6249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * then the Instances table will be regenerated. 6259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 626315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doUpdateTimezoneDependentFields() { 627ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 628315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneType = mCalendarCache.readTimezoneType(); 629315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Nothing to do if we have the "home" timezone type (timezone is sticky) 630a637bc824d92888eec9c6d2da0d5f1e594bebebaFabrice Di Meglio if (timezoneType != null && timezoneType.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 631315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 632315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 633315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // We are here in "auto" mode, the timezone is coming from the device 634ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (! isSameTimezoneDatabaseVersion()) { 635315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 636315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio doProcessEventRawTimes(localTimezone, TimeUtils.getTimeZoneDatabaseVersion()); 637ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 638315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isLocalSameAsInstancesTimezone()) { 639ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Even if the timezone hasn't changed, check for missed alarms. 640ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // This code executes when the CalendarProvider2 is created and 641ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // helps to catch missed alarms when the Calendar process is 642ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // killed (because of low-memory conditions) and then restarted. 643420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 644ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 645ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e) { 646f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 647f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "doUpdateTimezoneDependentFields() failed", e); 648f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 649ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio try { 650ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Clear at least the in-memory data (and if possible the 651ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // database fields) to force a re-computation of Instances. 652ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio mMetaData.clearInstanceRange(); 653ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } catch (SQLException e2) { 654f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 655f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "clearInstanceRange() also failed: " + e2); 656f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 657ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 6589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 659ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 660ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 661315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio protected void doProcessEventRawTimes(String localTimezone, String timeZoneDatabaseVersion) { 662ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.beginTransaction(); 663ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 6643443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio updateEventsStartEndFromEventRawTimesLocked(); 665ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateTimezoneDatabaseVersion(timeZoneDatabaseVersion); 666315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 667ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio regenerateInstancesTable(); 668ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.setTransactionSuccessful(); 669ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 670ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mDb.endTransaction(); 671ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 672ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 673ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 6743443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio private void updateEventsStartEndFromEventRawTimesLocked() { 6753443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio Cursor cursor = mDb.rawQuery(SQL_SELECT_EVENTSRAWTIMES, null /* selection args */); 676ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 677ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio while (cursor.moveToNext()) { 678ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio long eventId = cursor.getLong(0); 679ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtStart2445 = cursor.getString(1); 680ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String dtEnd2445 = cursor.getString(2); 6813443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio String eventTimezone = cursor.getString(3); 682f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (dtStart2445 == null && dtEnd2445 == null) { 683f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 684f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Event " + eventId + " has dtStart2445 and dtEnd2445 null " 685f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "at the same time in EventsRawTimes!"); 686f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 687f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio continue; 688f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 689ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio updateEventsStartEndLocked(eventId, 6903443e3ebeaa39e8415b43e7cf3b218caee554e9bFabrice Di Meglio eventTimezone, 691ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtStart2445, 692ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio dtEnd2445); 693ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 694ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } finally { 695ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor.close(); 696ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio cursor = null; 697ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 698ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 699ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 700ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private long get2445ToMillis(String timezone, String dt2445) { 701ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (null == dt2445) { 702f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 703f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.v(TAG, "Cannot parse null RFC2445 date"); 704f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 705ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 706ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 707ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Time time = (timezone != null) ? new Time(timezone) : new Time(); 708ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 709ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio time.parse(dt2445); 710ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (TimeFormatException e) { 711f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 712f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Cannot parse RFC2445 date " + dt2445); 713f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 714ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return 0; 715ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 716ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return time.toMillis(true /* ignore DST */); 717ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 718ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 719ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateEventsStartEndLocked(long eventId, 720ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio String timezone, String dtStart2445, String dtEnd2445) { 721ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 722ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio ContentValues values = new ContentValues(); 723b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTSTART, get2445ToMillis(timezone, dtStart2445)); 724b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio values.put(Events.DTEND, get2445ToMillis(timezone, dtEnd2445)); 725ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 726b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 727dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff new String[] {String.valueOf(eventId)}); 728ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (0 == result) { 729ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 730ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio Log.v(TAG, "Could not update Events table with values " + values); 731ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 732ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 733ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 734ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 735ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void updateTimezoneDatabaseVersion(String timeZoneDatabaseVersion) { 736ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio try { 737ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio mCalendarCache.writeTimezoneDatabaseVersion(timeZoneDatabaseVersion); 738ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } catch (CalendarCache.CacheException e) { 739f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 740f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Could not write timezone database version in the cache"); 741f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 742ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 743ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 7449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 745ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio /** 746ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio * Check if the time zone database version is the same as the cached one 747ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio */ 748ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected boolean isSameTimezoneDatabaseVersion() { 749315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 750315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 751ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return false; 752ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 753ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return TextUtils.equals(timezoneDatabaseVersion, TimeUtils.getTimeZoneDatabaseVersion()); 754ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 755ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 75625e5cdec4e39982fedcce0733d2b8ad1aa665b19Fabrice Di Meglio @VisibleForTesting 757ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio protected String getTimezoneDatabaseVersion() { 758315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneDatabaseVersion = mCalendarCache.readTimezoneDatabaseVersion(); 759315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneDatabaseVersion == null) { 760ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return ""; 761ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 762f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 763f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "timezoneDatabaseVersion = " + timezoneDatabaseVersion); 764f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 765ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio return timezoneDatabaseVersion; 766ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio } 767ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio 768315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private boolean isHomeTimezone() { 7690ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner final String type = mCalendarCache.readTimezoneType(); 7700ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner return CalendarCache.TIMEZONE_TYPE_HOME.equals(type); 771315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 772315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 773ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio private void regenerateInstancesTable() { 7749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // The database timezone is different from the current timezone. 7759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Regenerate the Instances table for this month. Include events 7769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // starting at the beginning of this month. 7779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long now = System.currentTimeMillis(); 778315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone = mCalendarCache.readTimezoneInstances(); 779315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 7809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(now); 7819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.monthDay = 1; 7829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.hour = 0; 7839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.minute = 0; 7849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.second = 0; 7851f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin = time.normalize(true); 7879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end = begin + MINIMUM_EXPANSION_SPAN; 7881f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio 7891f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio Cursor cursor = null; 7901f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio try { 7911f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor = handleInstanceQuery(new SQLiteQueryBuilder(), 7921f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio begin, end, 7931f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio new String[] { Instances._ID }, 7942ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* selection */, null, 7952ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik null /* sort */, 796d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio false /* searchByDayInsteadOfMillis */, 797315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio true /* force Instances deletion and expansion */, 7982ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik instancesTimezone, isHomeTimezone()); 7991f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } finally { 8001f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio if (cursor != null) { 8011f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio cursor.close(); 8021f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 8031f755da44aeecdc84d0e957d55178f942dfdb15dFabrice Di Meglio } 8049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 805420b7fb569773ae573fbe90c3a9c522d4c368863Erik mCalendarAlarm.rescheduleMissedAlarms(); 8069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 810b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected void notifyChange(boolean syncToNetwork) { 8119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Note that semantics are changed: notification is for CONTENT_URI, not the specific 8129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Uri that was modified. 813b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik mContentResolver.notifyChange(CalendarContract.CONTENT_URI, null, syncToNetwork); 8149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 816a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert /** 817a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert * ALERT table is maintained locally so don't request a sync for changes in it 818a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert */ 819a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert @Override 820a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert protected boolean shouldSyncFor(Uri uri) { 821a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert final int match = sUriMatcher.match(uri); 822a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert return !(match == CALENDAR_ALERTS || 823a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert match == CALENDAR_ALERTS_ID || 824a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert match == CALENDAR_ALERTS_BY_INSTANCE); 825a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert } 826a55c3aaa2b13b5350918efda98906afb8b6206c1Alon Albert 8279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 8289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 8299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String sortOrder) { 83096d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein final long identity = clearCallingIdentityInternal(); 8318015a4086559d9d025473c2e773b2f64888c2942Alon Albert try { 8328015a4086559d9d025473c2e773b2f64888c2942Alon Albert return queryInternal(uri, projection, selection, selectionArgs, sortOrder); 8338015a4086559d9d025473c2e773b2f64888c2942Alon Albert } finally { 83496d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein restoreCallingIdentityInternal(identity); 8358015a4086559d9d025473c2e773b2f64888c2942Alon Albert } 8368015a4086559d9d025473c2e773b2f64888c2942Alon Albert } 8378015a4086559d9d025473c2e773b2f64888c2942Alon Albert 8388015a4086559d9d025473c2e773b2f64888c2942Alon Albert private Cursor queryInternal(Uri uri, String[] projection, String selection, 8398015a4086559d9d025473c2e773b2f64888c2942Alon Albert String[] selectionArgs, String sortOrder) { 840ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 841ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query uri - " + uri); 8429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 8438d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 8449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final SQLiteDatabase db = mDbHelper.getReadableDatabase(); 8459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 8479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String groupBy = null; 8489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit = null; // Not currently implemented 849315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String instancesTimezone; 8509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 8529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 8539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 854fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, 8559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder); 856fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden case SYNCSTATE_ID: 857fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String selectionWithId = (SyncState._ID + "=?") 858fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden + (selection == null ? "" : " AND (" + selection + ")"); 859fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden // Prepend id to selectionArgs 860fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs = insertSelectionArg(selectionArgs, 861fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden String.valueOf(ContentUris.parseId(uri))); 862fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden return mDbHelper.getSyncState().query(db, projection, selectionWithId, 863fe1cb130bba78b36292a64d7c0bfb3292738973cAndy McFadden selectionArgs, sortOrder); 8649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 8659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 8661ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 8688d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8698d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8709ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 8719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 8729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 8731ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 8749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sEventsProjectionMap); 875636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 876b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 8779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 87819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 87919fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES: 88019fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 88119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 8828d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8838d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8849ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert selection = appendLastSyncedColumnToSelection(selection, uri); 88519fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 88619fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana case EVENT_ENTITIES_ID: 88719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setTables(CalendarDatabaseHelper.Views.EVENTS); 88819fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana qb.setProjectionMap(sEventEntitiesProjectionMap); 889636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 890b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 89119fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana break; 89219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 8932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 8942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setTables(Tables.COLORS); 8952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik qb.setProjectionMap(sColorsProjectionMap); 8968d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 8978d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 8982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 8992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 9009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 90143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES: 902b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 903b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert qb.setProjectionMap(sCalendarsProjectionMap); 9048d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 9058d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 9069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 90843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDAR_ENTITIES_ID: 909b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDARS); 910b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert qb.setProjectionMap(sCalendarsProjectionMap); 911636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 912b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ID); 9139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 9159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 9169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long begin; 9179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long end; 9189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff begin = Long.valueOf(uri.getPathSegments().get(2)); 9209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse begin " 9229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 9239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff end = Long.valueOf(uri.getPathSegments().get(3)); 9269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end " 9289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 930315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 9312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceQuery(qb, begin, end, projection, selection, selectionArgs, 9322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik sortOrder, match == INSTANCES_BY_DAY, false /* don't force an expansion */, 933315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 93481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH: 93581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang case INSTANCES_SEARCH_BY_DAY: 93681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 93781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang begin = Long.valueOf(uri.getPathSegments().get(2)); 93881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 93981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse begin " 94081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(2)); 94181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 94281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang try { 94381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang end = Long.valueOf(uri.getPathSegments().get(3)); 94481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } catch (NumberFormatException nfe) { 94581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang throw new IllegalArgumentException("Cannot parse end " 94681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang + uri.getPathSegments().get(3)); 94781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 948315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 94981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // this is already decoded 95081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String query = uri.getPathSegments().get(4); 9512ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return handleInstanceSearchQuery(qb, begin, end, query, projection, selection, 9522ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs, sortOrder, match == INSTANCES_SEARCH_BY_DAY, 953315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9546db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 9559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int startDay; 9569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int endDay; 9579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff startDay = Integer.valueOf(uri.getPathSegments().get(2)); 9599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse start day " 9619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(2)); 9629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 9639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 9649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff endDay = Integer.valueOf(uri.getPathSegments().get(3)); 9659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (NumberFormatException nfe) { 9669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Cannot parse end day " 9679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + uri.getPathSegments().get(3)); 9689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 969315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone = mCalendarCache.readTimezoneInstances(); 970315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return handleEventDayQuery(qb, startDay, endDay, projection, selection, 971315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, isHomeTimezone()); 9729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 97302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 975ef1f983b14a586f579a0d2978a0b0ccc2fcc425cMichael Chan qb.appendWhere(SQL_WHERE_ATTENDEE_BASE); 9769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 97802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.ATTENDEES + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sAttendeesProjectionMap); 980636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 981b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_ATTENDEES_ID); 9829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 984b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.REMINDERS); 9859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 98702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik qb.setTables(Tables.REMINDERS + ", " + Tables.EVENTS + ", " + Tables.CALENDARS); 9889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sRemindersProjectionMap); 989636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 990b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_REMINDERS_ID); 9919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 993b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 995b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 9969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 9979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 998b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 9999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 1000b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT); 10019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff groupBy = CalendarAlerts.EVENT_ID + "," + CalendarAlerts.BEGIN; 10029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 10039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 1004b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_ALERTS + ", " + CalendarDatabaseHelper.Views.EVENTS); 10059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sCalendarAlertsProjectionMap); 1006636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); 1007b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_CALENDAR_ALERT_ID); 10089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 10099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES: 1010b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 10119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 10129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 1013b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.EXTENDED_PROPERTIES); 1014636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff selectionArgs = insertSelectionArg(selectionArgs, uri.getPathSegments().get(1)); 1015b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_EXTENDED_PROPERTIES_ID); 10169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 1017315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1018b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.setTables(Tables.CALENDAR_CACHE); 1019315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio qb.setProjectionMap(sCalendarCacheProjectionMap); 1020315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio break; 10219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 10229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 10239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // run the query 10269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit); 10279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10298d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private void validateUriParameters(Set<String> queryParameterNames) { 10308d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert final Set<String> parameterNames = queryParameterNames; 10318d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert for (String parameterName : parameterNames) { 10328d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert if (!ALLOWED_URI_PARAMETERS.contains(parameterName)) { 10338d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert throw new IllegalArgumentException("Invalid URI parameter: " + parameterName); 10348d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10358d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10368d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert } 10378d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert 10389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, 10399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String selection, String[] selectionArgs, String sortOrder, String groupBy, 10409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String limit) { 1041ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio 104239c65e5716e21e863d8de587d139dae85f99422fFred Quintana if (projection != null && projection.length == 1 104339c65e5716e21e863d8de587d139dae85f99422fFred Quintana && BaseColumns._COUNT.equals(projection[0])) { 104439c65e5716e21e863d8de587d139dae85f99422fFred Quintana qb.setProjectionMap(sCountProjectionMap); 104539c65e5716e21e863d8de587d139dae85f99422fFred Quintana } 104639c65e5716e21e863d8de587d139dae85f99422fFred Quintana 1047ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 1048ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio Log.v(TAG, "query sql - projection: " + Arrays.toString(projection) + 1049ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selection: " + selection + 1050ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " selectionArgs: " + Arrays.toString(selectionArgs) + 1051ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " sortOrder: " + sortOrder + 1052ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " groupBy: " + groupBy + 1053ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio " limit: " + limit); 1054ab42ec67e77c398ac94ff1cf561fadd9f6b48dcbFabrice Di Meglio } 10559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, 10569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sortOrder, limit); 10579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c != null) { 10589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: is this the right notification Uri? 1059b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik c.setNotificationUri(mContentResolver, CalendarContract.Events.CONTENT_URI); 10609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return c; 10629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 10639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 10649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /* 10659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Fills the Instances table, if necessary, for the given range and then 10669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * queries the Instances table. 10679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 10689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param qb The query 10699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeBegin start of range (Julian days or ms) 10709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param rangeEnd end of range (Julian days or ms) 10719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param projection The projection 10729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param selection The selection 10739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param sort How to sort 10749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param searchByDay if true, range is in Julian days, if false, range is in ms 1075d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1076315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1077315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 10789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return 10799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 10809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private Cursor handleInstanceQuery(SQLiteQueryBuilder qb, long rangeBegin, 10812ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik long rangeEnd, String[] projection, String selection, String[] selectionArgs, 10822ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String sort, boolean searchByDay, boolean forceExpansion, 10832ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 10840ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 108581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 10869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff qb.setProjectionMap(sInstancesProjectionMap); 10879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (searchByDay) { 10889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Convert the first and last Julian day range to a range that uses 10899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // UTC milliseconds. 1090315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 10919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long beginMs = time.setJulianDay((int) rangeBegin); 10929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We add one to lastDay because the time is set to 12am on the given 10939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Julian day and we want to include all the events on the last day. 10949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long endMs = time.setJulianDay((int) rangeEnd + 1); 10959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1096315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true /* use minimum expansion window */, 1097315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1098b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 10999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 11009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // will lock the database. 1101315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, true /* use minimum expansion window */, 1102315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 1103b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 11049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11052ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11062ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String[] newSelectionArgs = new String[] {String.valueOf(rangeEnd), 11078335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String.valueOf(rangeBegin)}; 11082ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 11092ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = newSelectionArgs; 11102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 11112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik selectionArgs = combine(newSelectionArgs, selectionArgs); 11122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11138335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, null /* groupBy */, 11147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, sort); 11159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 11169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 111781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 11182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * Combine a set of arrays in the order they are passed in. All arrays must 11192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik * be of the same type. 11202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik */ 11212ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static <T> T[] combine(T[]... arrays) { 11222ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (arrays.length == 0) { 11232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik throw new IllegalArgumentException("Must supply at least 1 array to combine"); 11242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int totalSize = 0; 11272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 11282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize += array.length; 11292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik T[] finalArray = (T[]) (Array.newInstance(arrays[0].getClass().getComponentType(), 11322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik totalSize)); 11332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11342ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int currentPos = 0; 11352ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik for (T[] array : arrays) { 11362ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik int length = array.length; 11372ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik System.arraycopy(array, 0, finalArray, currentPos, length); 11382ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik currentPos += array.length; 11392ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11402ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik return finalArray; 11412ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 11422ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik 11432ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik /** 1144dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * Escape any special characters in the search token 1145dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @param token the token to escape 1146dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * @return the escaped token 1147dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang */ 1148dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang @VisibleForTesting 1149dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String escapeSearchToken(String token) { 1150dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_ESCAPE_PATTERN.matcher(token); 1151dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matcher.replaceAll(SEARCH_ESCAPE_CHAR + "$1"); 1152dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1153dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 1154dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang /** 115581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * Splits the search query into individual search tokens based on whitespace 1156dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * and punctuation. Leaves both single quoted and double quoted strings 1157dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang * intact. 115881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * 115981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @param query the search query 116081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * @return an array of tokens from the search query 116181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 116281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 116381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String[] tokenizeSearchQuery(String query) { 1164dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang List<String> matchList = new ArrayList<String>(); 1165dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang Matcher matcher = SEARCH_TOKEN_PATTERN.matcher(query); 1166dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String token; 1167dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang while (matcher.find()) { 1168dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang if (matcher.group(1) != null) { 1169dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // double quoted string 1170dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(1); 1171dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } else { 1172dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang // unquoted token 1173dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang token = matcher.group(); 1174dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1175dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang matchList.add(escapeSearchToken(token)); 1176dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang } 1177dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang return matchList.toArray(new String[matchList.size()]); 117881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 117981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 118081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang /** 118181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * In order to support what most people would consider a reasonable 118281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * search behavior, we have to do some interesting things here. We 118381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * assume that when a user searches for something like "lunch meeting", 118481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * they really want any event that matches both "lunch" and "meeting", 118581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * not events that match the string "lunch meeting" itself. In order to 118681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * do this across multiple columns, we have to construct a WHERE clause 118781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * that looks like: 118881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * <code> 118981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * WHERE (title LIKE "%lunch%" 119081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%lunch%" 119181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%lunch%") 119281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * AND (title LIKE "%meeting%" 119381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR description LIKE "%meeting%" 119481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * OR eventLocation LIKE "%meeting%") 119581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * </code> 119681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang * This "product of clauses" is a bit ugly, but produced a fairly good 1197cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * approximation of full-text search across multiple columns. The set 1198cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * of columns is specified by the SEARCH_COLUMNS constant. 1199cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * <p> 1200cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * Note the "WHERE" token isn't part of the returned string. The value 1201cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden * may be passed into a query as the "HAVING" clause. 120281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang */ 120381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 120481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String constructSearchWhere(String[] tokens) { 120581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (tokens.length == 0) { 120681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return ""; 120781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 120881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang StringBuilder sb = new StringBuilder(); 120981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang String column, token; 121081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 121181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("("); 121281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int i = 0; i < SEARCH_COLUMNS.length; i++) { 121381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append(SEARCH_COLUMNS[i]); 1214dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(" LIKE ? ESCAPE \""); 1215dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append(SEARCH_ESCAPE_CHAR); 1216dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang sb.append("\" "); 121781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (i < SEARCH_COLUMNS.length - 1) { 121881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang sb.append("OR "); 121981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 122081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 122118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(")"); 122218f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang if (j < tokens.length - 1) { 122318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang sb.append(" AND "); 122418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang } 122581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 122681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return sb.toString(); 122781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 122881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 122981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang @VisibleForTesting 1230ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak String[] constructSearchArgs(String[] tokens) { 123118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang int numCols = SEARCH_COLUMNS.length; 1232ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak int numArgs = tokens.length * numCols; 123318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang String[] selectionArgs = new String[numArgs]; 123481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang for (int j = 0; j < tokens.length; j++) { 1235ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak int start = numCols * j; 1236f50ca85e25d0e450b9f2ad78ee37870294462d4cMason Tang for (int i = start; i < start + numCols; i++) { 123718f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang selectionArgs[i] = "%" + tokens[j] + "%"; 123881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 123981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 124081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang return selectionArgs; 124181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 124281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 124381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang private Cursor handleInstanceSearchQuery(SQLiteQueryBuilder qb, 124481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long rangeBegin, long rangeEnd, String query, String[] projection, 12452ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selection, String[] selectionArgs, String sort, boolean searchByDay, 12462ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String instancesTimezone, boolean isHomeTimezone) { 12470ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 124818f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang qb.setTables(INSTANCE_SEARCH_QUERY_TABLES); 124981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setProjectionMap(sInstancesProjectionMap); 125081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 1251dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String[] tokens = tokenizeSearchQuery(query); 1252ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak String[] searchArgs = constructSearchArgs(tokens); 1253ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak String[] timeRange = new String[] {String.valueOf(rangeEnd), String.valueOf(rangeBegin)}; 12542ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik if (selectionArgs == null) { 1255ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak selectionArgs = combine(timeRange, searchArgs); 12562ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } else { 1257ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak // where clause comes first, so put selectionArgs before searchArgs. 1258ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak selectionArgs = combine(timeRange, selectionArgs, searchArgs); 12592ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik } 126018f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we pass this in as a HAVING instead of a WHERE so the filtering 126118f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // happens after the grouping 1262dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang String searchWhere = constructSearchWhere(tokens); 1263dc866a1a66871a55810cbf98169f3212fb47acd3Mason Tang 126481d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang if (searchByDay) { 126581d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Convert the first and last Julian day range to a range that uses 126681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // UTC milliseconds. 1267315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 126881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long beginMs = time.setJulianDay((int) rangeBegin); 126981d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // We add one to lastDay because the time is set to 12am on the given 127081d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // Julian day and we want to include all the events on the last day. 127181d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang long endMs = time.setJulianDay((int) rangeEnd + 1); 127281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 127318f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 127418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 127556292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(beginMs, endMs, 127656292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1277315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1278315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1279315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 128056292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1281b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 128281d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } else { 128381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang // will lock the database. 128418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // we expand the instances here because we might be searching over 128518f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang // a range where instance expansion has not occurred yet 128656292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio acquireInstanceRange(rangeBegin, rangeEnd, 128756292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio true /* use minimum expansion window */, 1288315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances deletion and expansion */, 1289315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio instancesTimezone, 1290315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio isHomeTimezone 129156292bcd683034ea05dd407ed15cebb70f954210Fabrice Di Meglio ); 1292b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN); 129381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 129418f75d6fe8dd0b4fb1deb5e56b4356ae6527bdbcMason Tang return qb.query(mDb, projection, selection, selectionArgs, 1295c3780839fd044b5d8109860b57a199a2da1d804fMichael Chan Tables.INSTANCES + "." + Instances._ID /* groupBy */, 1296cad6bc946434363f6ba6fed58bfa818cd6736d21Andy McFadden searchWhere /* having */, sort); 129781d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang } 129881d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang 12996db535b458146a279bebd4a51d56c1bdfc204528Erik private Cursor handleEventDayQuery(SQLiteQueryBuilder qb, int begin, int end, 1300315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String[] projection, String selection, String instancesTimezone, 1301315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean isHomeTimezone) { 13020ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 130381d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang qb.setTables(INSTANCE_QUERY_TABLES); 13046db535b458146a279bebd4a51d56c1bdfc204528Erik qb.setProjectionMap(sInstancesProjectionMap); 130543556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Convert the first and last Julian day range to a range that uses 130643556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // UTC milliseconds. 1307315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Time time = new Time(instancesTimezone); 1308192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long beginMs = time.setJulianDay(begin); 130943556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // We add one to lastDay because the time is set to 12am on the given 131043556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff // Julian day and we want to include all the events on the last day. 1311192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank long endMs = time.setJulianDay(end + 1); 131243556fa5610bd302cb80aa5ddc98af1e2f2d8b18Ken Shirriff 1313315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRange(beginMs, endMs, true, 1314315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio false /* do not force Instances expansion */, instancesTimezone, isHomeTimezone); 1315b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio qb.appendWhere(SQL_WHERE_INSTANCES_BETWEEN_DAY); 13168335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff String selectionArgs[] = new String[] {String.valueOf(end), String.valueOf(begin)}; 13178335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff 13188335a18ac6024f302b50e6f473ad4058cc355c85Ken Shirriff return qb.query(mDb, projection, selection, selectionArgs, 13196db535b458146a279bebd4a51d56c1bdfc204528Erik Instances.START_DAY /* groupBy */, null /* having */, null); 13209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 13249ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * table. Acquires the database lock and calls 13259ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #acquireInstanceRangeLocked(long, long, boolean, boolean, String, boolean)}. 13269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 13289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 13299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1330d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1331315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1332315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 13339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1334d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio private void acquireInstanceRange(final long begin, final long end, 1335315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final boolean useMinimumExpansionWindow, final boolean forceExpansion, 1336315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio final String instancesTimezone, final boolean isHomeTimezone) { 13379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 13389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 1339315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio acquireInstanceRangeLocked(begin, end, useMinimumExpansionWindow, 1340315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio forceExpansion, instancesTimezone, isHomeTimezone); 13419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 13429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 13439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 13449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 13489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Ensure that the date range given has all elements in the instance 13499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * table. The database lock must be held when calling this method. 13509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 13519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param begin start of range (ms) 13529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param end end of range (ms) 13539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param useMinimumExpansionWindow expand by at least MINIMUM_EXPANSION_SPAN 1354315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param forceExpansion force the Instance deletion and expansion if set to true 1355315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param instancesTimezone timezone we need to use for computing the instances 1356315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio * @param isHomeTimezone if true, we are in the "home" timezone 13579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 1358420b7fb569773ae573fbe90c3a9c522d4c368863Erik void acquireInstanceRangeLocked(long begin, long end, boolean useMinimumExpansionWindow, 1359315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean forceExpansion, String instancesTimezone, boolean isHomeTimezone) { 13609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandBegin = begin; 13619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long expandEnd = end; 13629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1363d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1364d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "acquireInstanceRange begin=" + begin + " end=" + end + 1365d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " useMin=" + useMinimumExpansionWindow + " force=" + forceExpansion); 1366d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1367d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1368315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (instancesTimezone == null) { 1369315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio Log.e(TAG, "Cannot run acquireInstanceRangeLocked() because instancesTimezone is null"); 1370315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return; 1371315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1372315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 13739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (useMinimumExpansionWindow) { 13749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // if we end up having to expand events into the instances table, expand 13759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events for a minimal amount of time, so we do not have to perform 13769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expansions frequently. 13779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long span = end - begin; 13789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (span < MINIMUM_EXPANSION_SPAN) { 13799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long additionalRange = (MINIMUM_EXPANSION_SPAN - span) / 2; 13809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandBegin -= additionalRange; 13819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff expandEnd += additionalRange; 13829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 13849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 13859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Check if the timezone has changed. 13869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We do this check here because the database is locked and we can 13879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // safely delete all the entries in the Instances table. 13889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff MetaData.Fields fields = mMetaData.getFieldsLocked(); 13899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long maxInstance = fields.maxInstance; 13909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long minInstance = fields.minInstance; 1391315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio boolean timezoneChanged; 1392315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone) { 1393315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = mCalendarCache.readTimezoneInstancesPrevious(); 1394315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(previousTimezone); 1395315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } else { 1396315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 1397315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio timezoneChanged = !instancesTimezone.equals(localTimezone); 13987be45683e367bd6897daf6444b03be938f8f5eaaErik // if we're in auto make sure we are using the device time zone 13997be45683e367bd6897daf6444b03be938f8f5eaaErik if (timezoneChanged) { 14007be45683e367bd6897daf6444b03be938f8f5eaaErik instancesTimezone = localTimezone; 14017be45683e367bd6897daf6444b03be938f8f5eaaErik } 1402315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 1403315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "home", then timezoneChanged only if current != previous 1404315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if "auto", then timezoneChanged, if !instancesTimezone.equals(localTimezone); 1405d69a1a64027cd5937c7db622aaf7af493e6d3610Fabrice Di Meglio if (maxInstance == 0 || timezoneChanged || forceExpansion) { 1406d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1407d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "Wiping instances and expanding from scratch"); 1408d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1409d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 14109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Empty the Instances table and expand from scratch. 1411b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL("DELETE FROM " + Tables.INSTANCES + ";"); 1412f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 14136db535b458146a279bebd4a51d56c1bdfc204528Erik Log.v(TAG, "acquireInstanceRangeLocked() deleted Instances," 14149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + " timezone changed: " + timezoneChanged); 14159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 1416f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, expandEnd, instancesTimezone); 1417315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 1418315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, expandBegin, expandEnd); 14199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14200ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner final String timezoneType = mCalendarCache.readTimezoneType(); 14217be45683e367bd6897daf6444b03be938f8f5eaaErik // This may cause some double writes but guarantees the time zone in 14227be45683e367bd6897daf6444b03be938f8f5eaaErik // the db and the time zone the instances are in is the same, which 14237be45683e367bd6897daf6444b03be938f8f5eaaErik // future changes may affect. 14247be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstances(instancesTimezone); 14257be45683e367bd6897daf6444b03be938f8f5eaaErik 14267be45683e367bd6897daf6444b03be938f8f5eaaErik // If we're in auto check if we need to fix the previous tz value 14270ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timezoneType)) { 14287be45683e367bd6897daf6444b03be938f8f5eaaErik String prevTZ = mCalendarCache.readTimezoneInstancesPrevious(); 14297be45683e367bd6897daf6444b03be938f8f5eaaErik if (TextUtils.equals(TIMEZONE_GMT, prevTZ)) { 14307be45683e367bd6897daf6444b03be938f8f5eaaErik mCalendarCache.writeTimezoneInstancesPrevious(instancesTimezone); 14317be45683e367bd6897daf6444b03be938f8f5eaaErik } 1432315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 14339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 14349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the desired range [begin, end] has already been 14379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // expanded, then simply return. The range is inclusive, that is, 14389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events that touch either endpoint are included in the expansion. 14399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This means that a zero-duration event that starts and ends at 14409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the endpoint will be included. 14419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We use [begin, end] here and not [expandBegin, expandEnd] for 14429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // checking the range because a common case is for the client to 14439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // request successive days or weeks, for example. If we checked 14449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that the expanded range [expandBegin, expandEnd] then we would 14459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // always be expanding because there would always be one more day 14469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // or week that hasn't been expanded. 14479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if ((begin >= minInstance) && (end <= maxInstance)) { 1448d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (DEBUG_INSTANCES) { 1449d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG + "-i", "instances are already expanded"); 1450d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1451f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 14529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "Canceled instance query (" + expandBegin + ", " + expandEnd 14539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + ") falls within previously expanded range."); 14549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 14569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested begin point has not been expanded, then include 14599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandBegin"). 14609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (begin < minInstance) { 1461f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(expandBegin, minInstance, instancesTimezone); 14629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff minInstance = expandBegin; 14639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the requested end point has not been expanded, then include 14669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // more events than requested in the expansion (use "expandEnd"). 14679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (end > maxInstance) { 1468f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.expandInstanceRangeLocked(maxInstance, expandEnd, instancesTimezone); 14699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff maxInstance = expandEnd; 14709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Update the bounds on the Instances table. 1473315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mMetaData.writeLocked(instancesTimezone, minInstance, maxInstance); 14749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 14759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 14769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 14779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public String getType(Uri url) { 14789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int match = sUriMatcher.match(url); 14799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 14809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 14819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event"; 14829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 14839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/event"; 14849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 14859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/reminder"; 14869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 14879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/reminder"; 14889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 14899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert"; 14909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_BY_INSTANCE: 14919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/calendar-alert-by-instance"; 14929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 14939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.item/calendar-alert"; 14949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 14959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 14966db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 14979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return "vnd.android.cursor.dir/event-instance"; 149848587d3291c4db7f0942e1bff55b88cfa7764ba0Erik case TIME: 149948587d3291c4db7f0942e1bff55b88cfa7764ba0Erik return "time/epoch"; 1500315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 1501315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return "vnd.android.cursor.dir/property"; 15029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 15039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + url); 15049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1507b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /** 1508b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Determines if the event is recurrent, based on the provided values. 1509b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1510b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden public static boolean isRecurrenceEvent(String rrule, String rdate, String originalId, 1511b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String originalSyncId) { 1512b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden return (!TextUtils.isEmpty(rrule) || 1513b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(rdate) || 1514b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalId) || 1515b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden !TextUtils.isEmpty(originalSyncId)); 15169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 15179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 1518646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik /** 1519646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * Takes an event and corrects the hrs, mins, secs if it is an allDay event. 1520d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 1521646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * AllDay events should have hrs, mins, secs set to zero. This checks if this is true and 1522d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * corrects the fields DTSTART, DTEND, and DURATION if necessary. 1523646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * 1524d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values The values to check and correct 1525d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param modValues Any updates will be stored here. This may be the same object as 1526d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <strong>values</strong>. 1527646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik * @return Returns true if a correction was necessary, false otherwise 1528646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik */ 1529d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean fixAllDayTime(ContentValues values, ContentValues modValues) { 1530499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden Integer allDayObj = values.getAsInteger(Events.ALL_DAY); 1531499287f0ccd3f20f8cf5f9007a9b422b825a7b7cAndy McFadden if (allDayObj == null || allDayObj == 0) { 1532d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return false; 1533d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1534d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1535646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik boolean neededCorrection = false; 1536646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1537d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtstart = values.getAsLong(Events.DTSTART); 1538d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long dtend = values.getAsLong(Events.DTEND); 1539d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String duration = values.getAsString(Events.DURATION); 1540d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Time time = new Time(); 1541d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String tempValue; 1542d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1543d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Change dtstart so h,m,s are 0 if necessary. 1544d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.clear(Time.TIMEZONE_UTC); 1545d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtstart.longValue()); 1546d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1547d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.hour = 0; 1548d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.minute = 0; 1549d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.second = 0; 1550d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTSTART, time.toMillis(true)); 1551d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1552d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1553d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1554d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If dtend exists for this event make sure it's h,m,s are 0. 1555d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (dtend != null) { 1556646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.clear(Time.TIMEZONE_UTC); 1557d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden time.set(dtend.longValue()); 1558646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik if (time.hour != 0 || time.minute != 0 || time.second != 0) { 1559646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.hour = 0; 1560646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.minute = 0; 1561646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik time.second = 0; 1562d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden dtend = time.toMillis(true); 1563d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DTEND, dtend); 1564646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik neededCorrection = true; 1565646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1566d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 1567646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1568d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (duration != null) { 1569d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int len = duration.length(); 1570d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* duration is stored as either "P<seconds>S" or "P<days>D". This checks if it's 1571d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * in the seconds format, and if so converts it to days. 1572d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 1573d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (len == 0) { 1574d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = null; 1575d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else if (duration.charAt(0) == 'P' && 1576d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration.charAt(len - 1) == 'S') { 1577d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int seconds = Integer.parseInt(duration.substring(1, len - 1)); 1578d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int days = (seconds + DAY_IN_SECONDS - 1) / DAY_IN_SECONDS; 1579d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden duration = "P" + days + "D"; 1580d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DURATION, duration); 1581d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden neededCorrection = true; 1582646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1583646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1584d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 1585646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik return neededCorrection; 1586646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 1587646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik 1588bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1589bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1590bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Determines whether the strings in the set name columns that may be overridden 1591bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * when creating a recurring event exception. 1592bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1593bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This uses a white list because it screens out unknown columns and is a bit safer to 1594bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * maintain than a black list. 1595bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1596bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private void checkAllowedInException(Set<String> keys) { 1597bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : keys) { 1598bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!ALLOWED_IN_EXCEPTION.contains(str.intern())) { 1599bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions can't overwrite " + str); 1600bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1601bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1602bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1603bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1604bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 160532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Splits a recurrent event at a specified instance. This is useful when modifying "this 160632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * and all future events". 160732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 160832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence rule has a COUNT specified, we need to split that at the point of the 160932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * exception. If the exception is instance N (0-based), the original COUNT is reduced 161032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * to N, and the exception's COUNT is set to (COUNT - N). 161132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 161232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * If the recurrence doesn't have a COUNT, we need to update or introduce an UNTIL value, 161332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the original recurrence will end just before the exception instance. (Note 161432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * that UNTIL dates are inclusive.) 161532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden *<p> 161632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * This should not be used to update the first instance ("update all events" action). 1617bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 161832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @param values The original event values; must include EVENT_TIMEZONE and DTSTART. 161932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * The RRULE value may be modified (with the expectation that this will propagate 162032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * into the exception event). 1621bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param endTimeMillis The time before which the event must end (i.e. the start time of the 1622bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event instance). 162332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * @return Values to apply to the original event. 1624bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1625bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static ContentValues setRecurrenceEnd(ContentValues values, long endTimeMillis) { 162632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden boolean origAllDay = values.getAsBoolean(Events.ALL_DAY); 162732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden String origRrule = values.getAsString(Events.RRULE); 1628bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 162932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence origRecurrence = new EventRecurrence(); 163032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.parse(origRrule); 1631bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 163232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Get the start time of the first instance in the original recurrence. 163332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long startTimeMillis = values.getAsLong(Events.DTSTART); 1634bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Time dtstart = new Time(); 1635bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden dtstart.timezone = values.getAsString(Events.EVENT_TIMEZONE); 163632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.set(startTimeMillis); 1637bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1638bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues updateValues = new ContentValues(); 163932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 164032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origRecurrence.count > 0) { 164132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden /* 164232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Generate the full set of instances for this recurrence, from the first to the 164332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * one just before endTimeMillis. The list should never be empty, because this method 164432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * should not be called for the first instance. All we're really interested in is 164532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * the *number* of instances found. 164632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden */ 164732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceSet recurSet = new RecurrenceSet(values); 164832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden RecurrenceProcessor recurProc = new RecurrenceProcessor(); 164932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden long[] recurrences; 165032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden try { 165132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden recurrences = recurProc.expand(dtstart, recurSet, startTimeMillis, endTimeMillis); 165232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } catch (DateException de) { 165332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException(de); 165432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 165532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 165632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (recurrences.length == 0) { 165732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden throw new RuntimeException("can't use this method on first instance"); 165832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 165932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 166032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden EventRecurrence excepRecurrence = new EventRecurrence(); 16611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden excepRecurrence.parse(origRrule); // TODO: add/use a copy constructor to EventRecurrence 166232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden excepRecurrence.count -= recurrences.length; 166332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden values.put(Events.RRULE, excepRecurrence.toString()); 166432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 166532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.count = recurrences.length; 166632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 166732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } else { 166832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden Time untilTime = new Time(); 166932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 167032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // The "until" time must be in UTC time in order for Google calendar 167132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // to display it properly. For all-day events, the "until" time string 167232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // must include just the date field, and not the time field. The 167332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // repeating events repeat up to and including the "until" time. 167432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.timezone = Time.TIMEZONE_UTC; 167532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 167632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // Subtract one second from the exception begin time to get the "until" time. 167732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.set(endTimeMillis - 1000); // subtract one second (1000 millis) 167832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden if (origAllDay) { 167932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.hour = untilTime.minute = untilTime.second = 0; 168032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.allDay = true; 168132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden untilTime.normalize(false); 168232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 168332aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // This should no longer be necessary -- DTSTART should already be in the correct 168432aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden // format for an all-day event. 168532aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.hour = dtstart.minute = dtstart.second = 0; 168632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.allDay = true; 168732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden dtstart.timezone = Time.TIMEZONE_UTC; 168832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 168932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden origRecurrence.until = untilTime.format2445(); 169032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden } 169132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden 169232aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden updateValues.put(Events.RRULE, origRecurrence.toString()); 1693bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden updateValues.put(Events.DTSTART, dtstart.normalize(true)); 1694bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return updateValues; 1695bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1696bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1697bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /** 1698bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Handles insertion of an exception to a recurring event. 1699bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <p> 1700bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * There are two modes, selected based on the presence of "rrule" in modValues: 1701bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <ol> 1702bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Create a single instance exception ("modify current event only"). 1703bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * <li> Cap the original event, and create a new recurring event ("modify this and all 1704bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * future events"). 1705bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * </ol> 1706bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * This may be used for "modify all instances of the event" by simply selecting the 1707bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * very first instance as the exception target. In that case, the ID of the "new" 1708bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * exception event will be the same as the originalEventId. 1709bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1710bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param originalEventId The _id of the event to be modified 1711bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @param modValues Event columns to update 1712c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * @param callerIsSyncAdapter Set if the content provider client is the sync adapter 1713bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * @return the ID of the new "exception" event, or -1 on failure 1714bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1715c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden private long handleInsertException(long originalEventId, ContentValues modValues, 1716c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden boolean callerIsSyncAdapter) { 1717bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1718bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.i(TAG, "RE: values: " + modValues.toString()); 1719bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1720bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1721bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Make sure they have specified an instance via originalInstanceTime. 1722bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Long originalInstanceTime = modValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1723bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime == null) { 1724bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden throw new IllegalArgumentException("Exceptions must specify " + 1725bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Events.ORIGINAL_INSTANCE_TIME); 1726bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1727bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1728bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Check for attempts to override values that shouldn't be touched. 1729bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden checkAllowedInException(modValues.keySet()); 1730bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1731c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden // If this isn't the sync adapter, set the "dirty" flag in any Event we modify. 1732c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (!callerIsSyncAdapter) { 1733c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.put(Events.DIRTY, true); 17347a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(modValues, Events.MUTATORS); 1735c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1736c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1737bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Wrap all database accesses in a transaction. 1738bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.beginTransaction(); 1739bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Cursor cursor = null; 1740bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1741bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that there's an instance corresponding to the specified time 1742bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (does this matter? it's weird, but not fatal?) 1743bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1744bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Grab the full set of columns for this event. 1745bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor = mDb.query(Tables.EVENTS, null /* columns */, 1746bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden SQL_WHERE_ID, new String[] { String.valueOf(originalEventId) }, 1747bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 1748bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor.getCount() != 1) { 1749bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event ID " + originalEventId + " lookup failed (count is " + 1750bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.getCount() + ")"); 1751bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1752bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1753bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden //DatabaseUtils.dumpCursor(cursor); 1754bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 17552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If there's a color index check that it's valid 1756387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_index = modValues.getAsString(Events.EVENT_COLOR_KEY); 17572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_index)) { 17582f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int calIdCol = cursor.getColumnIndex(Events.CALENDAR_ID); 17592f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calId = cursor.getLong(calIdCol); 17602f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 17612f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 17622f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calId != null) { 17632f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calId); 17642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 17652f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 17662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 17672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17682f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17692f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_index, Colors.TYPE_EVENT); 17702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 17712f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 1772bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1773bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Verify that the original event is in fact a recurring event by checking for the 1774bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * presence of an RRULE. If it's there, we assume that the event is otherwise 1775bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * properly constructed (e.g. no DTEND). 1776bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1777bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.moveToFirst(); 1778bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int rruleCol = cursor.getColumnIndex(Events.RRULE); 1779bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (TextUtils.isEmpty(cursor.getString(rruleCol))) { 1780bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event has no rrule"); 1781bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1782bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1783bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1784bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: old RRULE is " + cursor.getString(rruleCol)); 1785bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1786bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1787bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Verify that the original event is not itself a (single-instance) exception. 1788bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden int originalIdCol = cursor.getColumnIndex(Events.ORIGINAL_ID); 1789bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!TextUtils.isEmpty(cursor.getString(originalIdCol))) { 1790bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.e(TAG, "Original event is an exception"); 1791bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1792bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1793bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1794bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createSingleException = TextUtils.isEmpty(modValues.getAsString(Events.RRULE)); 1795bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1796bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: check for the presence of an existing exception on this event+instance? 1797bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The caller should be modifying that, not creating another exception. 1798bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Alternatively, we could do that for them.) 1799bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1800bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Create a new ContentValues for the new event. Start with the original event, 1801bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // and drop in the new caller-supplied values. This will set originalInstanceTime. 1802bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues values = new ContentValues(); 1803bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 1804f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor.close(); 1805f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden cursor = null; 1806bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1807b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: if we're changing this to an all-day event, we should ensure that 1808b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // hours/mins/secs on DTSTART are zeroed out (before computing DTEND). 1809b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // See fixAllDayTime(). 1810b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1811bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean createNewEvent = true; 1812bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createSingleException) { 1813bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1814bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Save a copy of a few fields that will migrate to new places. 1815bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1816bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _id = values.getAsString(Events._ID); 1817bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String _sync_id = values.getAsString(Events._SYNC_ID); 1818bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean allDay = values.getAsBoolean(Events.ALL_DAY); 1819bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1820bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1821bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Wipe out some fields that we don't want to clone into the exception event. 1822bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1823bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden for (String str : DONT_CLONE_INTO_EXCEPTION) { 1824bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(str); 1825bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1826bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1827bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1828bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Merge the new values on top of the existing values. Note this sets 1829bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * originalInstanceTime. 1830bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1831bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1832bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1833bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1834bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Copy some fields to their "original" counterparts: 1835bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id --> original_id 1836bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _sync_id --> original_sync_id 1837bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * allDay --> originalAllDay 1838bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1839bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this event hasn't been sync'ed with the server yet, the _sync_id field will 1840bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * be null. We will need to fill original_sync_id in later. (May not be able to 1841bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * do it right when our own _sync_id field gets populated, because the order of 1842bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * events from the server may not be what we want -- could update the exception 1843bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * before updating the original event.) 1844bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1845bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * _id is removed later (right before we write the event). 1846bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1847bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ID, _id); 1848bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_SYNC_ID, _sync_id); 1849bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.ORIGINAL_ALL_DAY, allDay); 1850bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1851bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Mark the exception event status as "tentative", unless the caller has some 1852bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // other value in mind (like STATUS_CANCELED). 1853bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (!values.containsKey(Events.STATUS)) { 1854bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.STATUS, Events.STATUS_TENTATIVE); 1855bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1856bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1857c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert // We're converting from recurring to non-recurring. 1858c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert // Clear out RRULE, RDATE, EXRULE & EXDATE 1859c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert // Replace DURATION with DTEND. 1860c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.remove(Events.RRULE); 1861c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert values.remove(Events.RDATE); 1862c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert values.remove(Events.EXRULE); 1863c817154d2185340a0d0d4b81f06e33e32ce81b37Alon Albert values.remove(Events.EXDATE); 1864bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1865bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Duration duration = new Duration(); 1866bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden String durationStr = values.getAsString(Events.DURATION); 1867bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden try { 1868bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden duration.parse(durationStr); 1869bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } catch (Exception ex) { 1870bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // NullPointerException if the original event had no duration. 1871bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // DateException if the duration was malformed. 1872bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Bad duration in recurring event: " + durationStr, ex); 1873bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1874bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1875bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1876c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1877c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * We want to compute DTEND as an offset from the start time of the instance. 1878c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If the caller specified a new value for DTSTART, we want to use that; if not, 1879c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the DTSTART in "values" will be the start time of the first instance in the 1880c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * recurrence, so we want to replace it with ORIGINAL_INSTANCE_TIME. 1881c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 1882c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long start; 1883c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.DTSTART)) { 1884c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.DTSTART); 1885c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } else { 1886c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden start = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 1887c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden values.put(Events.DTSTART, start); 1888c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1889bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.put(Events.DTEND, start + duration.getMillis()); 1890bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1891c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "RE: ORIG_INST_TIME=" + start + 1892c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ", duration=" + duration.getMillis() + 1893bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ", generated DTEND=" + values.getAsLong(Events.DTEND)); 1894bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 189585c09a31bcc3a18e173428bf7b628cec2834bebcAndy McFadden values.remove(Events.DURATION); 1896bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1897bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1898bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're going to "split" the recurring event, making the old one stop before 1899bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * this instance, and creating a new recurring event that starts here. 1900bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1901bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * No need to fill out the "original" fields -- the new event is not tied to 1902bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * the previous event in any way. 1903bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1904bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * If this is the first event in the series, we can just update the existing 1905bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event with the values. 1906bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1907bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden boolean canceling = (values.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 1908bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1909bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (originalInstanceTime.equals(values.getAsLong(Events.DTSTART))) { 1910bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1911bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Update fields in the existing event. Rather than use the merged data 1912bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * from the cursor, we just do the update with the new value set after 1913bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * removing the ORIGINAL_INSTANCE_TIME entry. 1914bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1915bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (canceling) { 1916bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: should we just call deleteEventInternal? 1917bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "Note: canceling entire event via exception call"); 1918bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1919bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1920bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: updating full event"); 1921bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1922ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(modValues)) { 1923ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 1924ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 1925ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 1926bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden modValues.remove(Events.ORIGINAL_INSTANCE_TIME); 1927bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 1928bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1929bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden createNewEvent = false; // skip event creation and related-table cloning 1930bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 1931bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1932bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: splitting event"); 1933bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1934bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1935bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 193632aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Cap the original event so it ends just before the target instance. In 193732aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * some cases (nonzero COUNT) this will also update the RRULE in "values", 193832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * so that the exception we're creating terminates appropriately. If a 193932aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * new RRULE was specified by the caller, the new rule will overwrite our 194032aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * changes when we merge the new values in below (which is the desired 194132aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * behavior). 1942bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1943bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden ContentValues splitValues = setRecurrenceEnd(values, originalInstanceTime); 1944bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.update(Tables.EVENTS, splitValues, SQL_WHERE_ID, 1945bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden new String[] { Long.toString(originalEventId) }); 1946bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1947bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 194832aa776d04075be5b5c945c68f7f352f4a3038b7Andy McFadden * Prepare the new event. We remove originalInstanceTime, because we're now 1949bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * creating a new event rather than an exception. 1950bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * 1951bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * We're always cloning a non-exception event (we tested to make sure the 1952bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * event doesn't specify original_id, and we don't allow original_id in the 1953bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * modValues), so we shouldn't end up creating a new event that looks like 1954bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * an exception. 1955bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1956bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.putAll(modValues); 1957bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events.ORIGINAL_INSTANCE_TIME); 1958bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1959c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 1960bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1961bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long newEventId; 1962bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (createNewEvent) { 1963bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden values.remove(Events._ID); // don't try to set this explicitly 1964be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 1965be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, null); 1966be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 1967be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 1968be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 1969bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1970bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = mDb.insert(Tables.EVENTS, null, values); 1971bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (newEventId < 0) { 1972bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Unable to add exception to recurring event"); 1973bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.w(TAG, "Values: " + values); 1974bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return -1; 1975bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1976bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (DEBUG_EXCEPTION) { 1977bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden Log.d(TAG, "RE: new ID is " + newEventId); 1978bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 1979bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 1980b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // TODO: do we need to do something like this? 1981b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden //updateEventRawTimesLocked(id, updatedValues); 1982b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1983b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 1984b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Force re-computation of the Instances associated with the recurrence event. 1985b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 1986b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, newEventId, true, mDb); 1987b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 1988bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden /* 1989bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden * Some of the other tables (Attendees, Reminders, ExtendedProperties) reference 1990c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Event ID. We need to copy the entries from the old event, filling in the 1991c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * new event ID, so that somebody doing a SELECT on those tables will find 1992c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * matching entries. 1993bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden */ 1994bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden CalendarDatabaseHelper.copyEventRelatedTables(mDb, newEventId, originalEventId); 1995c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 1996c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 1997c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * If we modified Event.selfAttendeeStatus, we need to keep the corresponding 1998c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * entry in the Attendees table in sync. 1999c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 2000c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 2001c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden /* 2002c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * Each Attendee is identified by email address. To find the entry that 2003c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * corresponds to "self", we want to compare that address to the owner of 2004c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden * the Calendar. We're expecting to find one matching entry in Attendees. 2005c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden */ 2006c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden long calendarId = values.getAsLong(Events.CALENDAR_ID); 2007f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden String accountName = getOwner(calendarId); 2008f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden 2009f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden if (accountName != null) { 2010c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden ContentValues attValues = new ContentValues(); 2011c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.put(Attendees.ATTENDEE_STATUS, 2012c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden modValues.getAsString(Events.SELF_ATTENDEE_STATUS)); 2013c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden 2014c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden if (DEBUG_EXCEPTION) { 2015c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Log.d(TAG, "Updating attendee status for event=" + newEventId + 2016c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden " name=" + accountName + " to " + 2017c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden attValues.getAsString(Attendees.ATTENDEE_STATUS)); 2018c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2019c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden int count = mDb.update(Tables.ATTENDEES, attValues, 2020c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden Attendees.EVENT_ID + "=? AND " + Attendees.ATTENDEE_EMAIL + "=?", 2021c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden new String[] { String.valueOf(newEventId), accountName }); 2022b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (count != 1 && count != 2) { 2023b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // We're only expecting one matching entry. We might briefly see 2024b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // two during a server sync. 20257148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik Log.e(TAG, "Attendee status update on event=" + newEventId 20267148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " touched " + count + " rows. Expected one or two rows."); 2027b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (false) { 2028b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden // This dumps PII in the log, don't ship with it enabled. 2029b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Cursor debugCursor = mDb.query(Tables.ATTENDEES, null, 2030b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.EVENT_ID + "=? AND " + 2031b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden Attendees.ATTENDEE_EMAIL + "=?", 2032b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden new String[] { String.valueOf(newEventId), accountName }, 2033b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden null, null, null); 2034b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden DatabaseUtils.dumpCursor(debugCursor); 20350332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan if (debugCursor != null) { 20360332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan debugCursor.close(); 20370332925aa9db8c4826327edd85030a4791b7a8e6Michael Chan } 2038b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden } 2039b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden throw new RuntimeException("Status update WTF"); 2040c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2041c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2042c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden } 2043bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } else { 2044b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden /* 2045b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden * Update any Instances changed by the update to this Event. 2046b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden */ 2047b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden mInstancesHelper.updateInstancesLocked(values, originalEventId, false, mDb); 2048bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden newEventId = originalEventId; 2049bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2050bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 2051bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.setTransactionSuccessful(); 2052bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return newEventId; 2053bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } finally { 2054bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden if (cursor != null) { 2055bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden cursor.close(); 2056bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2057bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden mDb.endTransaction(); 2058bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2059bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 2060bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden 2061222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden /** 2062222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Fills in the originalId column for previously-created exceptions to this event. If 2063222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this event is not recurring or does not have a _sync_id, this does nothing. 2064222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2065222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * The server might send exceptions before the event they refer to. When 2066222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * this happens, the originalId field will not have been set in the 2067222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * exception events (it's the recurrence events' _id field, so it can't be 2068222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * known until the recurrence event is created). When we add a recurrence 2069222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * event with a non-empty _sync_id field, we write that event's _id to the 2070222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * originalId field of any events whose originalSyncId matches _sync_id. 2071222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * <p> 2072222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * Note _sync_id is only expected to be unique within a particular calendar. 2073222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * 2074222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param id The ID of the Event 2075222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden * @param values Values for the Event being inserted 2076222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden */ 2077222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden private void backfillExceptionOriginalIds(long id, ContentValues values) { 2078222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String syncId = values.getAsString(Events._SYNC_ID); 2079222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rrule = values.getAsString(Events.RRULE); 2080222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String rdate = values.getAsString(Events.RDATE); 2081222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden String calendarId = values.getAsString(Events.CALENDAR_ID); 2082222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2083222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden if (TextUtils.isEmpty(syncId) || TextUtils.isEmpty(calendarId) || 2084222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden (TextUtils.isEmpty(rrule) && TextUtils.isEmpty(rdate))) { 2085222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden // Not a recurring event, or doesn't have a server-provided sync ID. 2086222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden return; 2087222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2088222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2089222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden ContentValues originalValues = new ContentValues(); 2090222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden originalValues.put(Events.ORIGINAL_ID, id); 2091222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden mDb.update(Tables.EVENTS, originalValues, 2092222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden Events.ORIGINAL_SYNC_ID + "=? AND " + Events.CALENDAR_ID + "=?", 2093222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden new String[] { syncId, calendarId }); 2094222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden } 2095222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 20969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 2097b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 2098ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 20999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "insertInTransaction: " + uri); 21009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21018d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 21020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 21030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_INSERT, uri, values, callerIsSyncAdapter, match, 21040739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik null /* selection */, null /* selection args */); 21050ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 21069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = 0; 21089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 21099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 2110bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case SYNCSTATE: 21119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.getSyncState().insert(mDb, values); 21129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 21139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS: 21147e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2115c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 21167a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(values, Events.MUTATORS); 21177e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 21189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 21198253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert if (values.containsKey(Events.ORIGINAL_SYNC_ID) 21208253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && values.containsKey(Events.ORIGINAL_INSTANCE_TIME) 21218253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert && Events.STATUS_CANCELED == values.getAsInteger(Events.STATUS)) { 21228253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // event is a canceled instance of a recurring event, it doesn't these 21238253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert // values but lets fake some to satisfy curious consumers. 21248253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert final long origStart = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 21258253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTSTART, origStart); 21268253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.DTEND, origStart); 21278253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert values.put(Events.EVENT_TIMEZONE, Time.TIMEZONE_UTC); 21288253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } else { 21298253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert throw new RuntimeException("DTSTART field missing from event"); 21308253a84ce7abf2fa1c662b735432a502f4ace96fAlon Albert } 21319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: do we really need to make a copy? 2133e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff ContentValues updatedValues = new ContentValues(values); 2134be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 2135be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(updatedValues, null); 2136be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } else { 2137be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(updatedValues); 2138be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 2139e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // updateLastDate must be after validation, to ensure proper last date computation 2140e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff updatedValues = updateLastDate(updatedValues); 21419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (updatedValues == null) { 21429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("Could not insert event."); 21439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // return null; 21449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Long calendar_id = updatedValues.getAsLong(Events.CALENDAR_ID); 21462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendar_id == null) { 21472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // validateEventData checks this for non-sync adapter 21482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // inserts 21492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("New events must specify a calendar id"); 21502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color is valid if it is being set 2152387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = updatedValues.getAsString(Events.EVENT_COLOR_KEY); 21532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 21542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(calendar_id); 21552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 21562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 21572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 21582f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 21592f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 21602f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21612f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, color_id, 21622f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_EVENT); 21632f251c778c06d21ed7693a70f4a1268ff929242eRoboErik updatedValues.put(Events.EVENT_COLOR, color); 21642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 21659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String owner = null; 21662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!updatedValues.containsKey(Events.ORGANIZER)) { 21672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 21689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: This isn't entirely correct. If a guest is adding a recurrence 21699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // exception to an event, the organizer should stay the original organizer. 21709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This value doesn't go to the server and it will get fixed on sync, 21719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // so it shouldn't really matter. 21729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner != null) { 21739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updatedValues.put(Events.ORGANIZER, owner); 21749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 21759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 217634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 217734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && !updatedValues.containsKey(Events.ORIGINAL_ID)) { 2178503a798e5f76ecce75607277292bd9a326ba79ecTony Mak long originalId = getOriginalId(updatedValues 2179503a798e5f76ecce75607277292bd9a326ba79ecTony Mak .getAsString(Events.ORIGINAL_SYNC_ID), 2180ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson updatedValues.getAsString(Events.CALENDAR_ID)); 218134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId != -1) { 218234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_ID, originalId); 218334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 218434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } else if (!updatedValues.containsKey(Events.ORIGINAL_SYNC_ID) 218534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik && updatedValues.containsKey(Events.ORIGINAL_ID)) { 218634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = getOriginalSyncId(updatedValues 218734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik .getAsLong(Events.ORIGINAL_ID)); 218834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (!TextUtils.isEmpty(originalSyncId)) { 218934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik updatedValues.put(Events.ORIGINAL_SYNC_ID, originalSyncId); 219034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 219134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 2192d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(updatedValues, updatedValues)) { 2193f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2194f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "insertInTransaction: " + 2195f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio "allDay is true but sec, min, hour were not 0."); 2196f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2197646444fdde3bde0a2ac948e021bc52b07c1d4a18Erik } 21981c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updatedValues.remove(Events.HAS_ALARM); // should not be set by caller 2199c4d44fd028e7f5f44f46439c3410dab3456e6d3fFabrice Di Meglio // Insert the row 22009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.eventsInsert(updatedValues); 22019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id != -1) { 22029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventRawTimesLocked(id, updatedValues); 2203f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik mInstancesHelper.updateInstancesLocked(updatedValues, id, 2204f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik true /* new event */, mDb); 22059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 22069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If we inserted a new event that specified the self-attendee 22079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status, then we need to add an entry to the attendees table. 22089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) { 22099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS); 22109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (owner == null) { 22112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik owner = getOwner(calendar_id); 22129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff createAttendeeEntry(id, status, owner); 22149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2215b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden 2216222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden backfillExceptionOriginalIds(id, values); 2217222f23bb26b6a72a9a0725593f456cfe497f7e91Andy McFadden 2218dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 22199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 22209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2221bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID: 2222bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long originalEventId = ContentUris.parseId(uri); 2223c832113820b3fe514077b45dc4daaae970ef3284Andy McFadden id = handleInsertException(originalEventId, values, callerIsSyncAdapter); 2224bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden break; 22259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 222682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden // TODO: verify that all required fields are present 22279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 22289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null && syncEvents == 1) { 2229c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 22309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String accountType = values.getAsString( 2231c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 22329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final Account account = new Account(accountName, accountType); 2233fa332ecedc0c340109811552407142f6e4f600b2RoboErik String eventsUrl = values.getAsString(Calendars.CAL_SYNC1); 22341b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl); 22359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2236387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String cal_color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 22372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(cal_color_id)) { 22382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 22392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 22402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = verifyColorExists(accountName, accountType, cal_color_id, 22412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Colors.TYPE_CALENDAR); 22422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Calendars.CALENDAR_COLOR, color); 22432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarsInsert(values); 2245dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(id, callerIsSyncAdapter); 22469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 22472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 22482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // verifyTransactionAllowed requires this be from a sync 22492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter, all of the required fields are marked NOT NULL in 22502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // the db. TODO Do we need explicit checks here or should we 22512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // just let sqlite throw if something isn't specified? 22522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = uri.getQueryParameter(Colors.ACCOUNT_NAME); 22532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = uri.getQueryParameter(Colors.ACCOUNT_TYPE); 2254387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String colorIndex = values.getAsString(Colors.COLOR_KEY); 22552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 22562f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Account name and type must be non" 22572f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " empty parameters for " + uri); 22582f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22592f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(colorIndex)) { 22602f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("COLOR_INDEX must be non empty for " + uri); 22612f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22622f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!values.containsKey(Colors.COLOR_TYPE) || !values.containsKey(Colors.COLOR)) { 22632f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException( 22642f251c778c06d21ed7693a70f4a1268ff929242eRoboErik "New colors must contain COLOR_TYPE and COLOR"); 22652f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Make sure the account we're inserting for is the same one the 22672f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // adapter is claiming to be. TODO should we throw if they 22682f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // aren't the same? 22692f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_NAME, accountName); 22702f251c778c06d21ed7693a70f4a1268ff929242eRoboErik values.put(Colors.ACCOUNT_TYPE, accountType); 22712f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 22722f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Verify the color doesn't already exist 22732f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 22742f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 22754755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan final long colorType = values.getAsLong(Colors.COLOR_TYPE); 22764755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 22772f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.getCount() != 0) { 22784755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("color type " + colorType 22794755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + " and index " + colorIndex 22807148c4fbb67fd9b20fb0b92d23e831b05ec22155RoboErik + " already exists for account and type provided"); 22812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 22832f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) 22842f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 22852f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 22862f251c778c06d21ed7693a70f4a1268ff929242eRoboErik id = mDbHelper.colorsInsert(values); 22872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 2288503a798e5f76ecce75607277292bd9a326ba79ecTony Mak case ATTENDEES: { 22899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Attendees.EVENT_ID)) { 22909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Attendees values must " 22919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + "contain an event_id"); 22929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2293503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 2294503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (!doesEventExist(eventIdObj)) { 2295503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Log.i(TAG, "Trying to insert a attendee to a non-existent event"); 2296503a798e5f76ecce75607277292bd9a326ba79ecTony Mak return null; 2297503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 22987e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 22999ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final Long eventId = values.getAsLong(Attendees.EVENT_ID); 23009ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 23019ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 23027e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23039ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.attendeesInsert(values); 23049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Copy the attendee status value to the Events table. 23069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff updateEventAttendeeStatus(mDb, values); 23079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2308503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2309503a798e5f76ecce75607277292bd9a326ba79ecTony Mak case REMINDERS: { 23101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 23111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventIdObj == null) { 23129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Reminders values must " 23131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "contain a numeric event_id"); 23149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2315503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (!doesEventExist(eventIdObj)) { 2316503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Log.i(TAG, "Trying to insert a reminder to a non-existent event"); 2317503a798e5f76ecce75607277292bd9a326ba79ecTony Mak return null; 2318503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2319503a798e5f76ecce75607277292bd9a326ba79ecTony Mak 23207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 23211c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventIdObj); 23221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setEventDirty(eventIdObj); 23237e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23249ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.remindersInsert(values); 23259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // We know this event has at least one reminder, so make sure "hasAlarm" is 1. 23271c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden setHasAlarm(eventIdObj, 1); 23281c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 23299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Schedule another event alarm, if necessary 23309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 23319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "insertInternal() changing reminder"); 23329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 2333ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 23349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 23351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 2336503a798e5f76ecce75607277292bd9a326ba79ecTony Mak case CALENDAR_ALERTS: { 2337503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 2338503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (eventIdObj == null) { 23399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("CalendarAlerts values must " 2340503a798e5f76ecce75607277292bd9a326ba79ecTony Mak + "contain a numeric event_id"); 2341503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2342503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (!doesEventExist(eventIdObj)) { 2343503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Log.i(TAG, "Trying to insert an alert to a non-existent event"); 2344503a798e5f76ecce75607277292bd9a326ba79ecTony Mak return null; 23459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff id = mDbHelper.calendarAlertsInsert(values); 23472fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 23482fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 23499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2350503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2351503a798e5f76ecce75607277292bd9a326ba79ecTony Mak case EXTENDED_PROPERTIES: { 2352503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Long eventIdObj = values.getAsLong(Reminders.EVENT_ID); 2353503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (eventIdObj == null) { 23549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("ExtendedProperties values must " 2355503a798e5f76ecce75607277292bd9a326ba79ecTony Mak + "contain a numeric event_id"); 2356503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2357503a798e5f76ecce75607277292bd9a326ba79ecTony Mak if (!doesEventExist(eventIdObj)) { 2358503a798e5f76ecce75607277292bd9a326ba79ecTony Mak Log.i(TAG, "Trying to insert extended properties to a non-existent event id = " 2359503a798e5f76ecce75607277292bd9a326ba79ecTony Mak + eventIdObj); 2360503a798e5f76ecce75607277292bd9a326ba79ecTony Mak return null; 23619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (!callerIsSyncAdapter) { 2363b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik final Long eventId = values 2364b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik .getAsLong(CalendarContract.ExtendedProperties.EVENT_ID); 23659ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDbHelper.duplicateEvent(eventId); 23669ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert setEventDirty(eventId); 23677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 23689ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert id = mDbHelper.extendedPropertiesInsert(values); 23699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff break; 2370503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 23713b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden case EMMA: 23723b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Special target used during code-coverage evaluation. 23733b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden handleEmmaRequest(values); 23743b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden break; 23759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 23769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 23779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 23789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EXTENDED_PROPERTIES_ID: 23799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 23809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 23816db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 2382315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 23837e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff throw new UnsupportedOperationException("Cannot insert into that URL: " + uri); 23849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 23859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 23869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 23889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (id < 0) { 23899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 23909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23919f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return ContentUris.withAppendedId(uri, id); 23929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 23939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2394503a798e5f76ecce75607277292bd9a326ba79ecTony Mak private boolean doesEventExist(long eventId) { 2395503a798e5f76ecce75607277292bd9a326ba79ecTony Mak return DatabaseUtils.queryNumEntries(mDb, Tables.EVENTS, Events._ID + "=?", 2396503a798e5f76ecce75607277292bd9a326ba79ecTony Mak new String[]{String.valueOf(eventId)}) > 0; 2397503a798e5f76ecce75607277292bd9a326ba79ecTony Mak } 2398503a798e5f76ecce75607277292bd9a326ba79ecTony Mak 2399e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 24003b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Handles special commands related to EMMA code-coverage testing. 24013b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 24023b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * @param values Parameters from the caller. 24033b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 24043b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static void handleEmmaRequest(ContentValues values) { 24053b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /* 24063b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * This is not part of the public API, so we can't share constants with the CTS 24073b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * test code. 24083b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * 24093b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * Bad requests, or attempting to request EMMA coverage data when the coverage libs 24103b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden * aren't linked in, will cause an exception. 24113b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden */ 24123b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String cmd = values.getAsString("cmd"); 24133b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden if (cmd.equals("start")) { 24143b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // We'd like to reset the coverage data, but according to FAQ item 3.14 at 24153b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // http://emma.sourceforge.net/faq.html, this isn't possible in 2.0. 24163b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage testing started"); 24173b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } else if (cmd.equals("stop")) { 24183b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // Call com.vladium.emma.rt.RT.dumpCoverageData() to cause a data dump. We 24193b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden // may not have been built with EMMA, so we need to do this through reflection. 24203b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden String filename = values.getAsString("outputFileName"); 24213b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 24223b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden File coverageFile = new File(filename); 24233b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden try { 24243b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); 24253b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", 24263b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden coverageFile.getClass(), boolean.class, boolean.class); 24273b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 24283b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden dumpCoverageMethod.invoke(null, coverageFile, false /*merge*/, 24293b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden false /*stopDataCollection*/); 24303b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden Log.d(TAG, "Emma coverage data written to " + filename); 24313b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } catch (Exception e) { 24323b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden throw new RuntimeException("Emma coverage dump failed", e); 24333b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 24343b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 24353b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden } 24363b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden 24373b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden /** 24385ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * Validates the recurrence rule, if any. We allow single- and multi-rule RRULEs. 2439ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * <p> 24405ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * TODO: Validate RDATE, EXRULE, EXDATE (possibly passing in an indication of whether we 24415ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden * believe we have the full set, so we can reject EXRULE when not accompanied by RRULE). 2442ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * 2443ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden * @return A boolean indicating successful validation. 2444ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden */ 2445ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden private boolean validateRecurrenceRule(ContentValues values) { 2446ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden String rrule = values.getAsString(Events.RRULE); 2447ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2448ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!TextUtils.isEmpty(rrule)) { 24495ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden String[] ruleList = rrule.split("\n"); 24505ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden for (String recur : ruleList) { 24515ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden EventRecurrence er = new EventRecurrence(); 24525ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden try { 24535ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden er.parse(recur); 24545ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } catch (EventRecurrence.InvalidFormatException ife) { 24555ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden Log.w(TAG, "Invalid recurrence rule: " + recur); 2456bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 24575ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden return false; 24585ce2a67ba623d3a32a2aa3bb70c5ded7e8fd7b5bAndy McFadden } 2459ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2460ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2461ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2462ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden return true; 2463ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2464ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden 2465bfea6da707f8d352432096371e7da76c230d9059Michael Chan private void dumpEventNoPII(ContentValues values) { 2466bfea6da707f8d352432096371e7da76c230d9059Michael Chan if (values == null) { 2467bfea6da707f8d352432096371e7da76c230d9059Michael Chan return; 2468bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2469bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2470bfea6da707f8d352432096371e7da76c230d9059Michael Chan StringBuilder bob = new StringBuilder(); 2471bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("dtStart: ").append(values.getAsLong(Events.DTSTART)); 2472bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndtEnd: ").append(values.getAsLong(Events.DTEND)); 2473bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nall_day: ").append(values.getAsInteger(Events.ALL_DAY)); 2474bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ntz: ").append(values.getAsString(Events.EVENT_TIMEZONE)); 2475bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\ndur: ").append(values.getAsString(Events.DURATION)); 2476bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrrule: ").append(values.getAsString(Events.RRULE)); 2477bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nrdate: ").append(values.getAsString(Events.RDATE)); 2478bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nlast_date: ").append(values.getAsLong(Events.LAST_DATE)); 2479bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2480bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nid: ").append(values.getAsLong(Events._ID)); 2481bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nsync_id: ").append(values.getAsString(Events._SYNC_ID)); 2482bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_id: ").append(values.getAsLong(Events.ORIGINAL_ID)); 2483bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_sync_id: ").append(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2484bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_inst_time: ").append(values.getAsLong(Events.ORIGINAL_INSTANCE_TIME)); 2485bfea6da707f8d352432096371e7da76c230d9059Michael Chan bob.append("\nori_all_day: ").append(values.getAsInteger(Events.ORIGINAL_ALL_DAY)); 2486bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2487bfea6da707f8d352432096371e7da76c230d9059Michael Chan Log.i(TAG, bob.toString()); 2488bfea6da707f8d352432096371e7da76c230d9059Michael Chan } 2489bfea6da707f8d352432096371e7da76c230d9059Michael Chan 2490ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden /** 249162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Do some scrubbing on event data before inserting or updating. In particular make 249262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * dtend, duration, etc make sense for the type of event (regular, recurrence, exception). 249362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * Remove any unexpected fields. 2494e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * 249562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param values the ContentValues to insert. 249662fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * @param modValues if non-null, explicit null entries will be added here whenever something 249762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden * is removed from <strong>values</strong>. 2498e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 249962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden private void scrubEventData(ContentValues values, ContentValues modValues) { 2500e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2501e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2502e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2503e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2504c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_SYNC_ID)); 2505e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null; 2506e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasRrule || hasRdate) { 2507e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence: 2508e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of first event 2509e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is null 2510e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is the duration of the event 2511ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden // rrule is a valid recurrence rule 2512e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the end of the last event or null if it repeats forever 2513e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2514e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2515ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden if (!validateRecurrenceRule(values)) { 2516ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2517ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden values.getAsString(Events.RRULE)); 2518ba54f5f9ca0c33fd518b1c87bb15fb7907672e04Andy McFadden } 2519e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) { 252062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DTEND, ORIGINAL_SYNC_ID, ORIGINAL_INSTANCE_TIME"); 2521e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 252262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence: " + values); 2523e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2524e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DTEND); 2525c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.remove(Events.ORIGINAL_SYNC_ID); 2526e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.ORIGINAL_INSTANCE_TIME); 252762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 252862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DTEND); 252962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_SYNC_ID); 253062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.ORIGINAL_INSTANCE_TIME); 253162fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2532e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2533e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else if (hasOriginalEvent || hasOriginalInstanceTime) { 2534e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Recurrence exception 2535e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is start time of exception event 2536e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is end time of exception event 2537e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2538e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2539e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastdate is same as dtend 2540e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is the _sync_id of the recurrence 2541e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is the start time of the event being replaced 2542e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) { 254362fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2544e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 254562fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for recurrence exception: " + values); 2546e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2547e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 254862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 254962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 255062fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2551e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2552e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } else { 2553e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // Regular event 2554e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtstart is the start time 2555e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // dtend is the end time 2556e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // duration is null 2557e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // rrule is null 2558e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // lastDate is the same as dtend 2559e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalEvent is null 2560e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff // originalInstanceTime is null 2561e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (!hasDtend || hasDuration) { 256262fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Scrubbing DURATION"); 2563e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 256462fb6911ea17d10de9662f455983ea045324aa62Andy McFadden Log.d(TAG, "Invalid values for event: " + values); 2565e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2566e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff values.remove(Events.DURATION); 256762fb6911ea17d10de9662f455983ea045324aa62Andy McFadden if (modValues != null) { 256862fb6911ea17d10de9662f455983ea045324aa62Andy McFadden modValues.putNull(Events.DURATION); 256962fb6911ea17d10de9662f455983ea045324aa62Andy McFadden } 2570e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2571e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2572e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff } 2573e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff 2574d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 2575d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Validates event data. Pass in the full set of values for the event (i.e. not just 2576d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * a part that's being updated). 2577d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2578d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values Event data. 2579d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws IllegalArgumentException if bad data is found. 2580d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 2581d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private void validateEventData(ContentValues values) { 258282b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.CALENDAR_ID))) { 258382b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include a calendar_id"); 258482b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 258582b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden if (TextUtils.isEmpty(values.getAsString(Events.EVENT_TIMEZONE))) { 258682b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden throw new IllegalArgumentException("Event values must include an eventTimezone"); 258782b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden } 258882b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden 2589d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtstart = values.getAsLong(Events.DTSTART) != null; 2590d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDtend = values.getAsLong(Events.DTEND) != null; 2591d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION)); 2592d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE)); 2593d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE)); 2594d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasRrule || hasRdate) { 2595d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!validateRecurrenceRule(values)) { 2596d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Invalid recurrence rule: " + 2597d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.getAsString(Events.RRULE)); 2598d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2599d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2600d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 2601d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDtstart) { 2602bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2603d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTSTART cannot be empty."); 2604d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2605d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!hasDuration && !hasDtend) { 2606bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2607d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("DTEND and DURATION cannot both be null for " + 2608d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "an event."); 2609d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2610d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (hasDuration && hasDtend) { 2611bfea6da707f8d352432096371e7da76c230d9059Michael Chan dumpEventNoPII(values); 2612d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Cannot have both DTEND and DURATION in an event"); 2613d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2614d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 2615d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 26169ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private void setEventDirty(long eventId) { 26177a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String mutators = DatabaseUtils.stringForQuery( 26187a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert mDb, 26197a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert SQL_QUERY_EVENT_MUTATORS, 26207a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert new String[]{String.valueOf(eventId)}); 26217a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String packageName = getCallingPackageName(); 26227a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String newMutators; 26237a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (TextUtils.isEmpty(mutators)) { 26247a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert newMutators = packageName; 26257a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } else { 26267a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String[] strings = mutators.split(","); 26277a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert boolean found = false; 26287a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert for (String string : strings) { 26297a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (string.equals(packageName)) { 26307a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert found = true; 26317a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert break; 26327a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 26337a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 26347a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (!found) { 26357a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert newMutators = mutators + "," + packageName; 26367a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } else { 26377a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert newMutators = mutators; 26387a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 26397a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 26407a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY_AND_MUTATORS, 26417a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert new Object[] {newMutators, eventId}); 26427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 26437e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 2644ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson private long getOriginalId(String originalSyncId, String calendarId) { 2645ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson if (TextUtils.isEmpty(originalSyncId) || TextUtils.isEmpty(calendarId)) { 264634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return -1; 264734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 264834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 264934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik long originalId = -1; 265034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 265134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 265234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, 2653ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson Events._SYNC_ID + "=?" + " AND " + Events.CALENDAR_ID + "=?", 2654ff5d02de9fddecbd5649f243233514e256a705c2Isaac Katzenelson new String[] {originalSyncId, calendarId}, null); 265534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 265634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalId = c.getLong(0); 265734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 265834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 265934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 266034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 266134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 266234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 266334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalId; 266434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 266534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 266634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik private String getOriginalSyncId(long originalId) { 266734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (originalId == -1) { 266834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return null; 266934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 267034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik // Get the original id for this event 267134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik String originalSyncId = null; 267234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Cursor c = null; 267334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik try { 267434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c = query(Events.CONTENT_URI, new String[] {Events._SYNC_ID}, 267534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik Events._ID + "=?", new String[] {Long.toString(originalId)}, null); 267634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null && c.moveToFirst()) { 267734c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik originalSyncId = c.getString(0); 267834c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 267934c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } finally { 268034c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik if (c != null) { 268134c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik c.close(); 268234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 268334c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 268434c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik return originalSyncId; 268534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik } 268634c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik 26874755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private Cursor getColorByTypeIndex(String accountName, String accountType, long colorType, 26884755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan String colorIndex) { 26894755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan return mDb.query(Tables.COLORS, COLORS_PROJECTION, COLOR_FULL_SELECTION, new String[] { 26904755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan accountName, accountType, Long.toString(colorType), colorIndex 26914755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan }, null, null, null); 26922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 26932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 26949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 2695f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * Gets a calendar's "owner account", i.e. the e-mail address of the owner of the calendar. 2696f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * 2697f029d7c00095e8fff6963f301ca85196b61525e3Andy McFadden * @param calId The calendar ID. 26989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @return email of owner or null 26999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 27009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private String getOwner(long calId) { 2701f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio if (calId < 0) { 2702f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.ERROR)) { 2703f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.e(TAG, "Calendar Id is not valid: " + calId); 2704f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2705f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio return null; 2706f09652d7327e45711f0e5b210e4df9c4c4c78ac4Fabrice Di Meglio } 27079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address of this user from this Calendar 27089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String emailAddress = null; 27099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 27109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 27119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 27129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 27139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 27149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 27159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 27169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2717f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2718f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2719f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 27209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 27219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff emailAddress = cursor.getString(0); 27239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 27249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 27259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 27269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return emailAddress; 27299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private Account getAccount(long calId) { 27322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = null; 27332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor cursor = null; 27342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 27352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 27362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ACCOUNT_PROJECTION, null /* selection */, null /* selectionArgs */, 27372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* sort */); 27382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor == null || !cursor.moveToFirst()) { 27392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (Log.isLoggable(TAG, Log.DEBUG)) { 27402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 27412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 27422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return null; 27432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 27442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik account = new Account(cursor.getString(ACCOUNT_NAME_INDEX), 27452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.getString(ACCOUNT_TYPE_INDEX)); 27462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 27472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) { 27482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 27492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 27502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 27512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return account; 27522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 27532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 27549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 27559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Creates an entry in the Attendees table that refers to the given event 27569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * and that has the given response status. 27579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 27589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param eventId the event id that the new entry in the Attendees table 27599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * should refer to 27609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param status the response status 27619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param emailAddress the email of the attendee 27629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 27639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void createAttendeeEntry(long eventId, int status, String emailAddress) { 27649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 27659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.EVENT_ID, eventId); 27669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_STATUS, status); 27679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE); 27689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // TODO: The relationship could actually be ORGANIZER, but it will get straightened out 27699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // on sync. 27709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_RELATIONSHIP, 27719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Attendees.RELATIONSHIP_ATTENDEE); 27729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Attendees.ATTENDEE_EMAIL, emailAddress); 27739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // We don't know the ATTENDEE_NAME but that will be filled in by the 27759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // server and sent back to us. 27769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.attendeesInsert(values); 27779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 27789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 27809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Updates the attendee status in the Events table to be consistent with 27819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * the value in the Attendees table. 27829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * 27839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * @param db the database 278424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param attendeeValues the column values for one row in the Attendees table. 27859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 27869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventAttendeeStatus(SQLiteDatabase db, ContentValues attendeeValues) { 27879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the event id for this attendee 278824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Long eventIdObj = attendeeValues.getAsLong(Attendees.EVENT_ID); 278924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (eventIdObj == null) { 279024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.w(TAG, "Attendee update values don't include an event_id"); 279124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return; 279224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 279324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = eventIdObj; 27949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 27959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (MULTIPLE_ATTENDEES_PER_EVENT) { 27969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the calendar id for this event 27979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = null; 27989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calId; 27999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 28009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Events.CONTENT_URI, eventId), 28019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Events.CALENDAR_ID }, 28029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 28039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 28049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 28059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2806f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2807f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + eventId + " in Events table"); 2808f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 28099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 28109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calId = cursor.getLong(0); 28129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 28139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 28149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 28159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the owner email for this Calendar 28199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarEmail = null; 28209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 28219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 28229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId), 28239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff new String[] { Calendars.OWNER_ACCOUNT }, 28249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 28259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 28269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 28279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor == null || !cursor.moveToFirst()) { 2828f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 2829f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "Couldn't find " + calId + " in Calendars table"); 2830f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 28319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 28329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff calendarEmail = cursor.getString(0); 28349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 28359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (cursor != null) { 28369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 28379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (calendarEmail == null) { 28419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 28429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Get the email address for this attendee 28459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String attendeeEmail = null; 28469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (attendeeValues.containsKey(Attendees.ATTENDEE_EMAIL)) { 28479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff attendeeEmail = attendeeValues.getAsString(Attendees.ATTENDEE_EMAIL); 28489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the attendee email does not match the calendar email, then this 28519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // attendee is not the owner of this calendar so we don't update the 28529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // selfAttendeeStatus in the event. 28539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!calendarEmail.equals(attendeeEmail)) { 28549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 28559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 285824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Select a default value for "status" based on the relationship. 28599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff int status = Attendees.ATTENDEE_STATUS_NONE; 286024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer relationObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); 286124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (relationObj != null) { 286224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int rel = relationObj; 28639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (rel == Attendees.RELATIONSHIP_ORGANIZER) { 28649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff status = Attendees.ATTENDEE_STATUS_ACCEPTED; 28659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 286824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // If the status is specified, use that. 286924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Integer statusObj = attendeeValues.getAsInteger(Attendees.ATTENDEE_STATUS); 287024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (statusObj != null) { 287124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden status = statusObj; 28729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 28749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues values = new ContentValues(); 28759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.SELF_ATTENDEE_STATUS, status); 2876b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio db.update(Tables.EVENTS, values, SQL_WHERE_ID, 2877b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(eventId)}); 28789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 28799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2880d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 28811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Set the "hasAlarm" column in the database. 28821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 28831c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param eventId The _id of the Event to update. 28841c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @param val The value to set it to (0 or 1). 28851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 28861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private void setHasAlarm(long eventId, int val) { 28871c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues values = new ContentValues(); 28881c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden values.put(Events.HAS_ALARM, val); 28891c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int count = mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, 28901c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 28911c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (count != 1) { 28921c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Log.w(TAG, "setHasAlarm on event " + eventId + " updated " + count + 28931c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden " rows (expected 1)"); 28941c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 28951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 28961c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 28971c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 2898d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Calculates the "last date" of the event. For a regular event this is the start time 2899d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * plus the duration. For a recurring event this is the start date of the last event in 2900d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence, plus the duration. The event recurs forever, this returns -1. If 2901d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the recurrence rule can't be parsed, this returns -1. 2902d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 2903d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values 2904d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the date, in milliseconds, since the start of the epoch (UTC), or -1 if an 2905d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * exceptional condition exists. 2906d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @throws DateException 2907d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 29089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long calculateLastDate(ContentValues values) 29099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throws DateException { 29109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Allow updates to some event fields like the title or hasAlarm 29119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // without requiring DTSTART. 29129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!values.containsKey(Events.DTSTART)) { 29139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (values.containsKey(Events.DTEND) || values.containsKey(Events.RRULE) 29149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.DURATION) 29159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EVENT_TIMEZONE) 29169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.RDATE) 29179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXRULE) 29189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff || values.containsKey(Events.EXDATE)) { 29199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new RuntimeException("DTSTART field missing from event"); 29209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return -1; 29229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long dtstartMillis = values.getAsLong(Events.DTSTART); 29249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long lastMillis = -1; 29259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Can we use dtend with a repeating event? What does that even 29279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // mean? 29289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // NOTE: if the repeating event has a dtend, we convert it to a 29299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // duration during event processing, so this situation should not 29309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // occur. 29319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtEnd = values.getAsLong(Events.DTEND); 29329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtEnd != null) { 29339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtEnd; 29349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 29359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // find out how long it is 29369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Duration duration = new Duration(); 29379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String durationStr = values.getAsString(Events.DURATION); 29389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (durationStr != null) { 29399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff duration.parse(durationStr); 29409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2942f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio RecurrenceSet recur = null; 2943f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio try { 2944f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio recur = new RecurrenceSet(values); 2945f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } catch (EventRecurrence.InvalidFormatException e) { 2946f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 2947f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not parse RRULE recurrence string: " + 2948b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik values.get(CalendarContract.Events.RRULE), e); 2949f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 2950d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: this should throw an exception or return a distinct error code 2951f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio return lastMillis; // -1 2952f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio } 29539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2954f8de1a9391de5d8b6a6a0ae7c55e1a2c318d6c05Fabrice Di Meglio if (null != recur && recur.hasRecurrence()) { 29559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is repeating, so find the last date it 29569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // could appear on 29579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String tz = values.getAsString(Events.EVENT_TIMEZONE); 29599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (TextUtils.isEmpty(tz)) { 29619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 29629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff tz = Time.TIMEZONE_UTC; 29639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time dtstartLocal = new Time(tz); 29659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff dtstartLocal.set(dtstartMillis); 29679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff RecurrenceProcessor rp = new RecurrenceProcessor(); 29699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = rp.getLastOccurence(dtstartLocal, recur); 29709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastMillis == -1) { 2971d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // repeats forever 29729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; // -1 29739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 29759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // the event is not repeating, just use dtstartMillis 29769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = dtstartMillis; 29779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // that was the beginning of the event. this is the end. 29809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff lastMillis = duration.addTo(lastMillis); 29819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return lastMillis; 29839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 2985e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff /** 2986e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * Add LAST_DATE to values. 298782b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * @param values the ContentValues (in/out); must include DTSTART and, if the event is 298882b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * recurring, the columns necessary to process a recurrence rule (RRULE, DURATION, 298982b6bf9d994d084fc8548279f3cf09eaae082430Andy McFadden * EVENT_TIMEZONE, etc). 2990e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff * @return values on success, null on failure 2991e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff */ 2992e604c19770482e181aa60a611b861ce5d8ed67d7Ken Shirriff private ContentValues updateLastDate(ContentValues values) { 29939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 29949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long last = calculateLastDate(values); 29959f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (last != -1) { 29969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff values.put(Events.LAST_DATE, last); 29979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 29989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 29999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return values; 30009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } catch (DateException e) { 30019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // don't add it if there was an error 3002f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3003f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Could not calculate last date.", e); 3004f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 30059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return null; 30069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3009d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 3010d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Creates or updates an entry in the EventsRawTimes table. 3011d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3012d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param eventId The ID of the event that was just created or is being updated. 3013d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param values For a new event, the full set of event values; for an updated event, 3014d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * the set of values that are being changed. 3015d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 30169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void updateEventRawTimesLocked(long eventId, ContentValues values) { 30179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ContentValues rawValues = new ContentValues(); 30189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3019b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.EVENT_ID, eventId); 30209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String timezone = values.getAsString(Events.EVENT_TIMEZONE); 30229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean allDay = false; 30249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer allDayInteger = values.getAsInteger(Events.ALL_DAY); 30259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 30269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDay = allDayInteger != 0; 30279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDay || TextUtils.isEmpty(timezone)) { 30309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // floating timezone 30319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff timezone = Time.TIMEZONE_UTC; 30329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Time time = new Time(timezone); 30359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 30369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtstartMillis = values.getAsLong(Events.DTSTART); 30379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtstartMillis != null) { 30389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtstartMillis); 3039b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTSTART_2445, time.format2445()); 30409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long dtendMillis = values.getAsLong(Events.DTEND); 30439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (dtendMillis != null) { 30449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(dtendMillis); 3045b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.DTEND_2445, time.format2445()); 30469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long originalInstanceMillis = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME); 30499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (originalInstanceMillis != null) { 30509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // This is a recurrence exception so we need to get the all-day 30519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // status of the original recurring event in order to format the 30529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // date correctly. 30539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff allDayInteger = values.getAsInteger(Events.ORIGINAL_ALL_DAY); 30549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (allDayInteger != null) { 30559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDayInteger != 0; 30569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(originalInstanceMillis); 3058b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.ORIGINAL_INSTANCE_TIME_2445, 3059b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio time.format2445()); 30609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Long lastDateMillis = values.getAsLong(Events.LAST_DATE); 30639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (lastDateMillis != null) { 30649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.allDay = allDay; 30659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff time.set(lastDateMillis); 3066b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik rawValues.put(CalendarContract.EventsRawTimes.LAST_DATE_2445, time.format2445()); 30679f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30699f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.eventsRawTimesReplace(rawValues); 30709f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30719f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 3073b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 3074b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio boolean callerIsSyncAdapter) { 3075ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 30769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "deleteInTransaction: " + uri); 30779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 30788d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 30799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff final int match = sUriMatcher.match(uri); 30800739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_DELETE, uri, null, callerIsSyncAdapter, match, 30810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 30820ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 30830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 30849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 30859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 30869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); 30879f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: 30892ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 30909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 30919323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3092dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3093dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3094dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().delete(mDb, selectionWithId, 3095dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs); 30969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 30972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 30988d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return deleteMatchingColors(appendAccountToSelection(uri, selection, 30998d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE), 31002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 31012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 31021ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS: 31039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31047e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int result = 0; 31058d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection( 31068d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert uri, selection, Events.ACCOUNT_NAME, Events.ACCOUNT_TYPE); 31077e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 31081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the ids to delete. 3109ab472739446ef9e4a6fdcf9903d6260741d96acfErik Pasternak Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION, 31101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff selection, selectionArgs, null /* groupBy */, 31117e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff null /* having */, null /* sortOrder */); 31129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 31131ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff while (cursor.moveToNext()) { 31141ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = cursor.getLong(0); 311510b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio result += deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 31169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 3117ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 3118dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 31199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 31209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor.close(); 31219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff cursor = null; 31229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 31249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31251ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff case EVENTS_ID: 31261ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff { 31271ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff long id = ContentUris.parseId(uri); 312810b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */); 31291ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 3130bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden case EXCEPTION_ID2: 3131bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden { 3132bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // This will throw NumberFormatException on missing or malformed input. 3133bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden List<String> segments = uri.getPathSegments(); 3134bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long eventId = Long.parseLong(segments.get(1)); 3135bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden long excepId = Long.parseLong(segments.get(2)); 3136bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // TODO: verify that this is an exception instance (has an ORIGINAL_ID field 3137bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // that matches the supplied eventId) 3138bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden return deleteEventInternal(excepId, callerIsSyncAdapter, false /* isBatch */); 3139bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden } 31409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES: 31419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31427e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3143b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, selection, selectionArgs); 31447e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 31451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, selection, 31461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 31477e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case ATTENDEES_ID: 31509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31517e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 31527e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff long id = ContentUris.parseId(uri); 3153b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.ATTENDEES, SQL_WHERE_ID, 3154b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 31557e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 31561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.ATTENDEES, uri, null /* selection */, 31572fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff null /* selectionArgs */); 31587e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS: 31619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, false, selection, selectionArgs, callerIsSyncAdapter); 31639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case REMINDERS_ID: 31659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteReminders(uri, true, null /*selection*/, null /*selectionArgs*/, 31671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden callerIsSyncAdapter); 31682fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31692fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES: 31702fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 31712fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 3172b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, selection, selectionArgs); 31732fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 31741c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, selection, 3175b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 31762fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31772fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 31782fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case EXTENDED_PROPERTIES_ID: 31792fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff { 31802fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff if (callerIsSyncAdapter) { 31812fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff long id = ContentUris.parseId(uri); 3182b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_ID, 3183636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 31842fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } else { 31851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.EXTENDED_PROPERTIES, uri, 31861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null /* selection */, null /* selectionArgs */); 31877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31899f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS: 31909f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 31917e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff if (callerIsSyncAdapter) { 3192b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, selection, selectionArgs); 31937e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } else { 31941c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return deleteFromEventRelatedTable(Tables.CALENDAR_ALERTS, uri, selection, 31951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs); 31967e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 31979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 31989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDAR_ALERTS_ID: 31999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 32002fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 32012fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 32029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 3203b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_ID, 3204b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio new String[] {String.valueOf(id)}); 32059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 32072ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik StringBuilder selectionSb = new StringBuilder(Calendars._ID + "="); 32089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(uri.getPathSegments().get(1)); 32099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!TextUtils.isEmpty(selection)) { 32109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(" AND ("); 32119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(selection); 32129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selectionSb.append(')'); 32139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff selection = selectionSb.toString(); 321524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // $FALL-THROUGH$ - fall through to CALENDARS for the actual delete 32169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS: 32178d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 32188d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 321974ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return deleteMatchingCalendars(selection, selectionArgs); 32209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES: 32219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case INSTANCES_BY_DAY: 32226db535b458146a279bebd4a51d56c1bdfc204528Erik case EVENT_DAYS: 3223315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: 32249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new UnsupportedOperationException("Cannot delete that URL"); 32259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 32269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 32279f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 32299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 323010b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio private int deleteEventInternal(long id, boolean callerIsSyncAdapter, boolean isBatch) { 32311ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff int result = 0; 3232192b1807d4b6265a4f7581580bd6172dae3fc1b1Marc Blank String selectionArgs[] = new String[] {String.valueOf(id)}; 32331ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 32341ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // Query this event to get the fields needed for deleting. 3235b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio Cursor cursor = mDb.query(Tables.EVENTS, EVENTS_PROJECTION, 3236b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio SQL_WHERE_ID, selectionArgs, 3237636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff null /* groupBy */, 32381ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff null /* having */, null /* sortOrder */); 32391ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff try { 32401ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff if (cursor.moveToNext()) { 32411ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff result = 1; 32421ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String syncId = cursor.getString(EVENTS_SYNC_ID_INDEX); 324348f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio boolean emptySyncId = TextUtils.isEmpty(syncId); 32441ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 32451ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // If this was a recurring event or a recurrence 32461ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // exception, then force a recalculation of the 32471ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff // instances. 32481ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rrule = cursor.getString(EVENTS_RRULE_INDEX); 32491ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff String rdate = cursor.getString(EVENTS_RDATE_INDEX); 3250b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origId = cursor.getString(EVENTS_ORIGINAL_ID_INDEX); 3251b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden String origSyncId = cursor.getString(EVENTS_ORIGINAL_SYNC_ID_INDEX); 3252b09eb917f2490a1dae20709a667df845a2e67c94Andy McFadden if (isRecurrenceEvent(rrule, rdate, origId, origSyncId)) { 32531ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff mMetaData.clearInstanceRange(); 32541ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 32554d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden boolean isRecurrence = !TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate); 32561ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 325748f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // we clean the Events and Attendees table if the caller is CalendarSyncAdapter 325848f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio // or if the event is local (no syncId) 3259bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // 3260bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // The EVENTS_CLEANUP_TRIGGER_SQL trigger will remove all associated data 3261bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (Attendees, Instances, Reminders, etc). 326248f38786c5eef920ff47bf08718be3ff94b68993Fabrice Di Meglio if (callerIsSyncAdapter || emptySyncId) { 3263b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs); 32644d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 32654d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // If this is a recurrence, and the event was never synced with the server, 32664d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we want to delete any exceptions as well. (If it has been to the server, 32674d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // we'll let the sync adapter delete the events explicitly.) We assume that, 32684d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // if the recurrence hasn't been synced, the exceptions haven't either. 32694d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden if (isRecurrence && emptySyncId) { 32704d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID, selectionArgs); 32714d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden } 32721ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } else { 3273bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // Event is on the server, so we "soft delete", i.e. mark as deleted so that 3274bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the sync adapter has a chance to tell the server about the deletion. After 3275bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // the server sees the change, the sync adapter will do the "hard delete" 3276bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden // (above). 32771ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff ContentValues values = new ContentValues(); 32781b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio values.put(Events.DELETED, 1); 3279c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Events.DIRTY, 1); 32807a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(values, Events.MUTATORS); 3281b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs); 328202494e34ecc44c1557a9929cdaef24d603e63450Fabrice Di Meglio 32834d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // Exceptions that have been synced shouldn't be deleted -- the sync 32844d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // adapter will take care of that -- but we want to "soft delete" them so 32854d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // that they will be removed from the instances list. 32864d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // TODO: this seems to confuse the sync adapter, and leaves you with an 32874d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // invisible "ghost" event after the server sync. Maybe we can fix 32884d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // this by making instance generation smarter? Not vital, since the 32894d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exception instances disappear after the server sync. 32904d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden //mDb.update(Tables.EVENTS, values, SQL_WHERE_ORIGINAL_ID_HAS_SYNC_ID, 32914d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // selectionArgs); 32924d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 32934d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // It's possible for the original event to be on the server but have 32944d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // exceptions that aren't. We want to remove all events with a matching 32954d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden // original_id and an empty _sync_id. 32964d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID_NO_SYNC_ID, 32974d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden selectionArgs); 32984d10d2da7bef342c2f5dcbfd91cc51a569a3998fAndy McFadden 329943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // Delete associated data; attendees, however, are deleted with the actual event 330043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // so that the sync adapter is able to notify attendees of the cancellation. 3301b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, selectionArgs); 3302b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EVENTS_RAW_TIMES, SQL_WHERE_EVENT_ID, selectionArgs); 3303b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.REMINDERS, SQL_WHERE_EVENT_ID, selectionArgs); 3304b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs); 3305b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID, 3306b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio selectionArgs); 33071ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 33081ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 33091ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } finally { 33101ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor.close(); 33111ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff cursor = null; 33121ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 33138f4ccb20cce4cd09bd9e0c777d2d5cd92a2c9b78Ken Shirriff 331410b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio if (!isBatch) { 3315ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 3316dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 331710b51a19b296eac6c43608a7a57fb910b0e5e8bcFabrice Di Meglio } 33181ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff return result; 33191ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff } 33201ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 33217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 33221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Delete rows from an Event-related table (e.g. Attendees) and mark corresponding events 33231c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * as dirty. 33241c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 33257e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param table The table to delete from 33267e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param uri The URI specifying the rows 33277e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selection for the query 33287e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * @param selectionArgs for the query 33297e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 33301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteFromEventRelatedTable(String table, Uri uri, String selection, 33311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden String[] selectionArgs) { 33321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (table.equals(Tables.EVENTS)) { 33331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("Don't delete Events with this method " 33341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden + "(use deleteEventInternal)"); 33351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33361c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 33381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 33397a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(dirtyValues, Events.MUTATORS); 33401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Re-issue the delete URI as a query. Note that, if this is a by-ID request, the ID 33431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * will be in the URI, not selection/selectionArgs. 33441c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 33451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Note that the query will return data according to the access restrictions, 33461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * so we don't need to worry about deleting data we don't have permission to read. 33471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, ID_PROJECTION, selection, selectionArgs, GENERIC_EVENT_ID); 33497e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 33507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 33511c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long prevEventId = -1; 33521c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 33531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long id = c.getLong(ID_INDEX); 33541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = c.getLong(EVENT_ID_INDEX); 33551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Duplicate the event. As a minor optimization, don't try to duplicate an 33561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // event that we just duplicated on the previous iteration. 33571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 33581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 33591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33609ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert mDb.delete(table, SQL_WHERE_ID, new String[]{String.valueOf(id)}); 33611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (eventId != prevEventId) { 33621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 33631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId)} ); 33641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 3365ea28dfc327c87b24855f7abd9a48ba9a1b3f43f5Tony Mak prevEventId = eventId; 33667e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 33677e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33687e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 33697e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 33707e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33717e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 33727e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 33737e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 33747e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff /** 33751c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Deletes rows from the Reminders table and marks the corresponding events as dirty. 33761c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Ensures the hasAlarm column in the Event is updated. 33771c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 33781c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * @return The number of rows deleted. 33791c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33801c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden private int deleteReminders(Uri uri, boolean byId, String selection, String[] selectionArgs, 33811c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden boolean callerIsSyncAdapter) { 33821c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33831c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this is a by-ID URI, make sure we have a good ID. Also, confirm that the 33841c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * selection is null, since we will be ignoring it. 33851c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 33861c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long rowId = -1; 33871c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 33881c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!TextUtils.isEmpty(selection)) { 33891c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 33901c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33911c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden rowId = ContentUris.parseId(uri); 33921c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (rowId < 0) { 33931c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 33941c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33951c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 33961c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 33971c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 33981c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Determine the set of events affected by this operation. There can be multiple 33991c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders with the same event_id, so to avoid beating up the database with "how many 34001c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * reminders are left" and "duplicate this event" requests, we want to generate a list 34011c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * of affected event IDs and work off that. 34021c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * 34031c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * TODO: use GROUP BY to reduce the number of rows returned in the cursor. (The content 34041c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * provider query() doesn't take it as an argument.) 34051c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 34061c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden HashSet<Long> eventIdSet = new HashSet<Long>(); 34071c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor c = query(uri, new String[] { Attendees.EVENT_ID }, selection, selectionArgs, null); 34081c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden try { 34091c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (c.moveToNext()) { 34101c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden eventIdSet.add(c.getLong(0)); 34111c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34121c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } finally { 34131c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden c.close(); 34141c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34151c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34161c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 34171c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * If this isn't a sync adapter, duplicate each event (along with its associated tables), 34181c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * and mark each as "dirty". This is for the benefit of partial-update sync. 34191c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 34201c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (!callerIsSyncAdapter) { 34211c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues dirtyValues = new ContentValues(); 34221c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden dirtyValues.put(Events.DIRTY, "1"); 34237a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(dirtyValues, Events.MUTATORS); 34241c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34251c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 34261c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 34271c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 34281c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDbHelper.duplicateEvent(eventId); 34291c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 34301c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 34311c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34321c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34331c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34341c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 34351c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * Issue the original deletion request. If we were called with a by-ID URI, generate 34361c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * a selection. 34371c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 34381c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (byId) { 34391c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selection = SQL_WHERE_ID; 34401c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 34411c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34421c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int delCount = mDb.delete(Tables.REMINDERS, selection, selectionArgs); 34431c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34441c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 34451c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * For each event, set "hasAlarm" to zero if we've deleted the last of the reminders. 34461c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * (If the event still has reminders, hasAlarm should already be 1.) Because we're 34471c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * executing in an exclusive transaction there's no risk of racing against other 34481c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * database updates. 34491c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 34501c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden ContentValues noAlarmValues = new ContentValues(); 34511c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden noAlarmValues.put(Events.HAS_ALARM, 0); 34521c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Iterator<Long> iter = eventIdSet.iterator(); 34531c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden while (iter.hasNext()) { 34541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden long eventId = iter.next(); 34551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden // Count up the number of reminders still associated with this event. 34571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden Cursor reminders = mDb.query(Tables.REMINDERS, new String[] { GENERIC_ID }, 34581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden SQL_WHERE_EVENT_ID, new String[] { String.valueOf(eventId) }, 34591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden null, null, null); 34601c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden int reminderCount = reminders.getCount(); 34611c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden reminders.close(); 34621c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34631c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden if (reminderCount == 0) { 34641c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden mDb.update(Tables.EVENTS, noAlarmValues, SQL_WHERE_ID, 34651c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden new String[] { String.valueOf(eventId) }); 34661c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34671c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34681c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34691c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden return delCount; 34701c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden } 34711c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 34721c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /** 347324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Update rows in a table and, if this is a non-sync-adapter update, mark the corresponding 347424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * events as dirty. 347524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * <p> 347624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * This only works for tables that are associated with an event. It is assumed that the 347724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * link to the Event row is a numeric identifier in a column called "event_id". 347824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 347924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param uri The original request URI. 348024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param byId Set to true if the URI is expected to include an ID. 348124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param updateValues The new values to apply. Not all columns need be represented. 348224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selection For non-by-ID operations, the "where" clause to use. 348324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param selectionArgs For non-by-ID operations, arguments to apply to the "where" clause. 348424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @param callerIsSyncAdapter Set to true if the caller is a sync adapter. 348524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * @return The number of rows updated. 34867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff */ 348724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden private int updateEventRelatedTable(Uri uri, String table, boolean byId, 348824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues updateValues, String selection, String[] selectionArgs, 348924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden boolean callerIsSyncAdapter) 349024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden { 349124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 349224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Confirm that the request has either an ID or a selection, but not both. It's not 349324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * actually "wrong" to have both, but it's not useful, and having neither is likely 349424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * a mistake. 349524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 349624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * If they provided an ID in the URI, convert it to an ID selection. 349724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 349824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (byId) { 349924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!TextUtils.isEmpty(selection)) { 350024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection not allowed for " + uri); 350124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 350224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long rowId = ContentUris.parseId(uri); 350324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (rowId < 0) { 350424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new IllegalArgumentException("ID expected but not found in " + uri); 350524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 350624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selection = SQL_WHERE_ID; 350724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs = new String[] { String.valueOf(rowId) }; 350824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } else { 350924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (TextUtils.isEmpty(selection)) { 351024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new UnsupportedOperationException("Selection is required for " + uri); 351124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 351224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 351324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 351424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 351524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * Query the events to update. We want all the columns from the table, so we us a 351624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * null projection. 351724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 351824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Cursor c = mDb.query(table, null /*projection*/, selection, selectionArgs, 351924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, null); 35207e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff int count = 0; 35217e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff try { 352224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (c.getCount() == 0) { 352324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.d(TAG, "No query results for " + uri + ", selection=" + selection + 352424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 352524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return 0; 352624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 352724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 352824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues dirtyValues = null; 352924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 353024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues = new ContentValues(); 353124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden dirtyValues.put(Events.DIRTY, "1"); 35327a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(dirtyValues, Events.MUTATORS); 353324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 353424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 353524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int idIndex = c.getColumnIndex(GENERIC_ID); 353624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden final int eventIdIndex = c.getColumnIndex(GENERIC_EVENT_ID); 353724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (idIndex < 0 || eventIdIndex < 0) { 353824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden throw new RuntimeException("Lookup on _id/event_id failed for " + uri); 353924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 354024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 354124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 354224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * For each row found: 354324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - merge original values with update values 354424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update database 354524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - if not sync adapter, set "dirty" flag in corresponding event to 1 354624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * - update Event attendee status 354724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 354824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden while (c.moveToNext()) { 354924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* copy the original values into a ContentValues, then merge the changes in */ 355024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden ContentValues values = new ContentValues(); 355124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden DatabaseUtils.cursorRowToContentValues(c, values); 355224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden values.putAll(updateValues); 355324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 355424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long id = c.getLong(idIndex); 355524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden long eventId = c.getLong(eventIdIndex); 355624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 355724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden // Make a copy of the original, so partial-update code can see diff. 355824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDbHelper.duplicateEvent(eventId); 355924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 356024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(table, values, SQL_WHERE_ID, new String[] { String.valueOf(id) }); 356124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (!callerIsSyncAdapter) { 356224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden mDb.update(Tables.EVENTS, dirtyValues, SQL_WHERE_ID, 356324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden new String[] { String.valueOf(eventId) }); 356424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 35657e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff count++; 356624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 356724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden /* 356824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * The Events table has a "selfAttendeeStatus" field that usually mirrors the 356924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * "attendeeStatus" column of one row in the Attendees table. It's the provider's 357024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * job to keep these in sync, so we have to check for changes here. (We have 357124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * to do it way down here because this is the only point where we have the 357224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * merged Attendees values.) 357324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 357424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * It's possible, but not expected, to have multiple Attendees entries with 357524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * matching attendeeEmail. The behavior in this case is not defined. 357624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * 357724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * We could do this more efficiently for "bulk" updates by caching the Calendar 357824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden * owner email and checking it here. 357924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden */ 358024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden if (table.equals(Tables.ATTENDEES)) { 358124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden updateEventAttendeeStatus(mDb, values); 35828ca3274cd6bb8cf43992f9be6725c89d770011a7Sara Ting sendUpdateNotification(eventId, callerIsSyncAdapter); 358324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden } 35847e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 35857e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } finally { 35867e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff c.close(); 35877e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 35887e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 35897e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff } 35907e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 35912f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int deleteMatchingColors(String selection, String[] selectionArgs) { 35922f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // query to find all the colors that match, for each 35932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - verify no one references it 35942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // - delete color 35952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, null, 35962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null, null); 35972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c == null) { 35982f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return 0; 35992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 36012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c2 = null; 36022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 36032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String index = c.getString(COLORS_COLOR_INDEX_INDEX); 36042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = c.getString(COLORS_ACCOUNT_NAME_INDEX); 36052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = c.getString(COLORS_ACCOUNT_TYPE_INDEX); 36062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean isCalendarColor = c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 36072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 36082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (isCalendarColor) { 36092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = mDb.query(Tables.CALENDARS, ID_ONLY_PROJECTION, 36102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik SQL_WHERE_CALENDAR_COLOR, new String[] { 36112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName, accountType, index 36122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }, null, null, null); 36132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 36142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 36152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " calendars."); 36162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 36172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 36192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2 = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, SQL_WHERE_EVENT_COLOR, 36202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] {accountName, accountType, index}, null); 36212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2.getCount() != 0) { 36222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new UnsupportedOperationException("Cannot delete color " + index 36232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + ". Referenced by " + c2.getCount() + " events."); 36242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 36252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 36282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c2 != null) { 36292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c2.close(); 36302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 36342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 36352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 36362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return mDb.delete(Tables.COLORS, selection, selectionArgs); 36392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 36402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 364174ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio private int deleteMatchingCalendars(String selection, String[] selectionArgs) { 36429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // query to find all the calendars that match, for each 36439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar subscription 36449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // - delete calendar 364574ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio Cursor c = mDb.query(Tables.CALENDARS, sCalendarsIdProjection, selection, 364674ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio selectionArgs, 364774ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* groupBy */, 364874ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* having */, 364974ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio null /* sortOrder */); 36509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (c == null) { 36519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return 0; 36529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 36549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 36559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = c.getLong(CALENDARS_INDEX_ID); 36569f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, false /* not selected */); 36579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 36599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 36609f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 366174ca9ba319a55a7dcb222344d2582e4dabe5d3bfFabrice Di Meglio return mDb.delete(Tables.CALENDARS, selection, selectionArgs); 36629f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 36639f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 3664fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio private boolean doesEventExistForSyncId(String syncId) { 3665fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (syncId == null) { 3666fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 3667fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio Log.w(TAG, "SyncID cannot be null: " + syncId); 3668fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3669fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return false; 3670fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3671fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio long count = DatabaseUtils.longForQuery(mDb, SQL_SELECT_COUNT_FOR_SYNC_ID, 3672fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio new String[] { syncId }); 3673fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio return (count > 0); 3674fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3675fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3676fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Check if an UPDATE with STATUS_CANCEL means that we will need to do an Update (instead of 3677fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // a Deletion) 3678fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3679fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // Deletion will be done only and only if: 3680fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event status = canceled 3681fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // - event is a recurrence exception that does not have its original (parent) event anymore 3682fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // 3683fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // This is due to the Server semantics that generate STATUS_CANCELED for both creation 3684fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // and deletion of a recurrence exception 3685fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio // See bug #3218104 3686d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden private boolean doesStatusCancelUpdateMeanUpdate(ContentValues values, 3687d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues modValues) { 3688d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isStatusCanceled = modValues.containsKey(Events.STATUS) && 3689d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden (modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED); 3690fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio if (isStatusCanceled) { 3691d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String originalSyncId = values.getAsString(Events.ORIGINAL_SYNC_ID); 3692d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3693d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!TextUtils.isEmpty(originalSyncId)) { 3694d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This event is an exception. See if the recurring event still exists. 3695d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return doesEventExistForSyncId(originalSyncId); 3696d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3697d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3698d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This is the normal case, we just want an UPDATE 3699d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return true; 3700d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3701d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 37022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private int handleUpdateColors(ContentValues values, String selection, String[] selectionArgs) { 37032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 37042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int result = mDb.update(Tables.COLORS, values, selection, selectionArgs); 37052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (values.containsKey(Colors.COLOR)) { 37062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 37072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, 37082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik null /* groupBy */, null /* having */, null /* orderBy */); 37092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik while (c.moveToNext()) { 37102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik boolean calendarColor = 37112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR; 37122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color = c.getInt(COLORS_COLOR_INDEX); 37132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String[] args = { 37142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_NAME_INDEX), 37152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_ACCOUNT_TYPE_INDEX), 37162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.getString(COLORS_COLOR_INDEX_INDEX) 37172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik }; 37182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik ContentValues colorValue = new ContentValues(); 37192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (calendarColor) { 37202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Calendars.CALENDAR_COLOR, color); 3721d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.CALENDARS, colorValue, SQL_WHERE_CALENDAR_COLOR, args); 37222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } else { 37232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik colorValue.put(Events.EVENT_COLOR, color); 3724d5af586101b6111ca188bb373098309c7c8a4abbAlon Albert mDb.update(Tables.EVENTS, colorValue, SQL_WHERE_EVENT_COLOR, args); 37252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 37282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 37292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 37302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37322f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37332f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return result; 37342f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 37352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 3736d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3737d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /** 3738d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Handles a request to update one or more events. 3739d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * <p> 3740d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * The original event(s) will be loaded from the database, merged with the new values, 3741d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * and the result checked for validity. In some cases this will alter the supplied 3742d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * arguments (e.g. zeroing out the times on all-day events), change additional fields (e.g. 3743d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * update LAST_DATE when DTSTART changes), or cause modifications to other tables (e.g. reset 3744d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * Instances when a recurrence rule changes). 3745d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3746d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param cursor The set of events to update. 37474b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden * @param updateValues The changes to apply to each event. 3748d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @param callerIsSyncAdapter Indicates if the request comes from the sync adapter. 3749d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * @return the number of rows updated 3750d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 37514b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden private int handleUpdateEvents(Cursor cursor, ContentValues updateValues, 3752d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean callerIsSyncAdapter) { 3753d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden /* 37541c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * This field is considered read-only. It should not be modified by applications or 37551c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden * by the sync adapter. 37561c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden */ 37571c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden updateValues.remove(Events.HAS_ALARM); 37581c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden 37591c72909abbfe7559bcc880c339399f1eaa0478f3Andy McFadden /* 3760d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For a single event, we can just load the event, merge modValues in, perform any 3761d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * fix-ups (putting changes into modValues), check validity, and then update(). We have 3762d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * to be careful that our fix-ups don't confuse the sync adapter. 3763d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * 3764d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * For multiple events, we need to load, merge, and validate each event individually. 3765d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * If no single-event-specific changes need to be made, we could just issue the original 3766d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * bulk update, which would be more efficient than a series of individual updates. 3767d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * However, doing so would prevent us from taking advantage of the partial-update 3768d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden * mechanism. 3769d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden */ 3770d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (cursor.getCount() > 1) { 3771d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3772d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "Performing update on " + cursor.getCount() + " events"); 3773d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3774d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3775d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden while (cursor.moveToNext()) { 37769f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Make a copy of updateValues so we can make some local changes. 37774b9f67cdc442ba0caa5bb007a4e0dfd3594ef945Andy McFadden ContentValues modValues = new ContentValues(updateValues); 37789f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 37799f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Load the event into a ContentValues object. 3780d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden ContentValues values = new ContentValues(); 3781d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden DatabaseUtils.cursorRowToContentValues(cursor, values); 37829f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden boolean doValidate = false; 37839f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (!callerIsSyncAdapter) { 37849f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden try { 37859f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Check to see if the data in the database is valid. If not, we will skip 37869f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // validation of the update, so that we don't blow up on attempts to 37879f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // modify existing badly-formed events. 37889f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden validateEventData(values); 37899f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden doValidate = true; 37909f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } catch (IllegalArgumentException iae) { 37919f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden Log.d(TAG, "Event " + values.getAsString(Events._ID) + 37929f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden " malformed, not validating update (" + 37939f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden iae.getMessage() + ")"); 37949f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 37959f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 37969f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden 37979f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Merge the modifications in. 3798d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden values.putAll(modValues); 3799d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 38002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // If a color_index is being set make sure it's valid 3801387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = modValues.getAsString(Events.EVENT_COLOR_KEY); 38022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 38032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = null; 38042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = null; 38052f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = mDb.query(Tables.CALENDARS, ACCOUNT_PROJECTION, SQL_WHERE_ID, 38062f251c778c06d21ed7693a70f4a1268ff929242eRoboErik new String[] { values.getAsString(Events.CALENDAR_ID) }, null, null, null); 38072f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 38082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c.moveToFirst()) { 38092f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = c.getString(ACCOUNT_NAME_INDEX); 38102f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = c.getString(ACCOUNT_TYPE_INDEX); 38112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38122f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 38132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 38142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 38152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_EVENT); 38182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 38192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 38209f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden // Scrub and/or validate the combined event. 3821be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden if (callerIsSyncAdapter) { 3822be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden scrubEventData(values, modValues); 38239f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden } 38249f97cde4a34eb814b2e14f694c349c5ad6003a6dAndy McFadden if (doValidate) { 3825be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden validateEventData(values); 3826be4ac5fac63f1619df46977891a6b4a3a0e02563Andy McFadden } 3827d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3828d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Look for any updates that could affect LAST_DATE. It's defined as the end of 3829d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // the last meeting, so we need to pay attention to DURATION. 3830d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3831d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DTEND) || 3832d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.DURATION) || 3833d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EVENT_TIMEZONE) || 3834d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RRULE) || 3835d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.RDATE) || 3836d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXRULE) || 3837d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.EXDATE)) { 3838d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long newLastDate; 3839d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 3840d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden newLastDate = calculateLastDate(values); 3841d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } catch (DateException de) { 3842d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Unable to compute LAST_DATE", de); 3843d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3844d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Long oldLastDateObj = values.getAsLong(Events.LAST_DATE); 3845d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long oldLastDate = (oldLastDateObj == null) ? -1 : oldLastDateObj; 3846d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (oldLastDate != newLastDate) { 3847d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // This overwrites any caller-supplied LAST_DATE. This is okay, because the 3848d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // caller isn't supposed to be messing with the LAST_DATE field. 3849d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (newLastDate < 0) { 3850d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.putNull(Events.LAST_DATE); 3851d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3852d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.LAST_DATE, newLastDate); 3853fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3854fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3855d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3856d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3857d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3858d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.put(Events.DIRTY, 1); 38597a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(modValues, Events.MUTATORS); 3860d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3861fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 3862d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Disallow updating the attendee status in the Events 3863d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // table. In the future, we could support this but we 3864d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // would have to query and update the attendees table 3865d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to keep the values consistent. 3866d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.SELF_ATTENDEE_STATUS)) { 3867d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden throw new IllegalArgumentException("Updating " 3868d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + Events.SELF_ATTENDEE_STATUS 3869d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden + " in Events table is not allowed."); 3870d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3871d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3872d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (fixAllDayTime(values, modValues)) { 3873d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.WARN)) { 3874d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.w(TAG, "handleUpdateEvents: " + 3875d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden "allDay is true but sec, min, hour were not 0."); 3876fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3877d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3878d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3879d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // For taking care about recurrences exceptions cancelations, check if this needs 3880d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // to be an UPDATE or a DELETE 3881d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden boolean isUpdate = doesStatusCancelUpdateMeanUpdate(values, modValues); 3882d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3883d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = values.getAsLong(Events._ID); 3884d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3885d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (isUpdate) { 3886d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If a user made a change, possibly duplicate the event so we can do a partial 3887d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // update. If a sync adapter made a change and that change marks an event as 3888d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // un-dirty, remove any duplicates that may have been created earlier. 3889d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (!callerIsSyncAdapter) { 3890d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.duplicateEvent(id); 3891d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3892d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DIRTY) 3893d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden && modValues.getAsInteger(Events.DIRTY) == 0) { 38947a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert modValues.put(Events.MUTATORS, (String) null); 3895d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDbHelper.removeDuplicateEvent(id); 3896d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3897d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3898d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden int result = mDb.update(Tables.EVENTS, modValues, SQL_WHERE_ID, 3899d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden new String[] { String.valueOf(id) }); 3900d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (result > 0) { 3901d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden updateEventRawTimesLocked(id, modValues); 3902d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mInstancesHelper.updateInstancesLocked(modValues, id, 3903d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden false /* not a new event */, mDb); 3904d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3905d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // XXX: should we also be doing this when RRULE changes (e.g. instances 3906d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // are introduced or removed?) 3907d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.DTSTART) || 3908d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.containsKey(Events.STATUS)) { 3909d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // If this is a cancellation knock it out 3910d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // of the instances table 3911d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (modValues.containsKey(Events.STATUS) && 3912d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden modValues.getAsInteger(Events.STATUS) == Events.STATUS_CANCELED) { 3913d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden String[] args = new String[] {String.valueOf(id)}; 3914d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden mDb.delete(Tables.INSTANCES, SQL_WHERE_EVENT_ID, args); 3915d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3916d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3917d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // The start time or status of the event changed, so run the 3918d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // event alarm scheduler. 3919d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (Log.isLoggable(TAG, Log.DEBUG)) { 3920d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Log.d(TAG, "updateInternal() changing event"); 3921d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3922ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 3923d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3924d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3925d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(id, callerIsSyncAdapter); 3926d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 3927d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } else { 3928d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden deleteEventInternal(id, callerIsSyncAdapter, true /* isBatch */); 3929ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 3930d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden sendUpdateNotification(callerIsSyncAdapter); 3931fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3932fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3933d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden 3934d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return cursor.getCount(); 3935fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 3936fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio 39379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff @Override 39389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff protected int updateInTransaction(Uri uri, ContentValues values, String selection, 3939b7c010fdc02695b692cd74acf432e8ccb3bda70cFabrice Di Meglio String[] selectionArgs, boolean callerIsSyncAdapter) { 3940ae270e35e14b5c7a756050cb8dcccf5771743850Fabrice Di Meglio if (Log.isLoggable(TAG, Log.VERBOSE)) { 39419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.v(TAG, "updateInTransaction: " + uri); 39429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39438d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert validateUriParameters(uri.getQueryParameterNames()); 39440739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final int match = sUriMatcher.match(uri); 39450739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyTransactionAllowed(TRANSACTION_UPDATE, uri, values, callerIsSyncAdapter, match, 39460739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik selection, selectionArgs); 39470ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 39489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff switch (match) { 39509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE: 39519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return mDbHelper.getSyncState().update(mDb, values, 39528d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 39538d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE), selectionArgs); 39549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case SYNCSTATE_ID: { 39568d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert selection = appendAccountToSelection(uri, selection, Calendars.ACCOUNT_NAME, 39578d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_TYPE); 39582ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik String selectionWithId = (SyncState._ID + "=?") 3959dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff + (selection == null ? "" : " AND (" + selection + ")"); 39609323bb1bbb247bac4871595a3de387ec7568897eKen Shirriff // Prepend id to selectionArgs 3961dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff selectionArgs = insertSelectionArg(selectionArgs, 3962dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff String.valueOf(ContentUris.parseId(uri))); 3963dc538177512191886cc40bc5e5125aae9bb197aaKen Shirriff return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); 39649f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 39659f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 39662f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 396710651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan int validValues = 0; 396810651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan if (values.getAsInteger(Colors.COLOR) != null) { 396910651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan validValues++; 397010651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan } 397110651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan if (values.getAsString(Colors.DATA) != null) { 397210651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan validValues++; 397310651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan } 397410651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan 397510651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan if (values.size() != validValues) { 397610651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan throw new UnsupportedOperationException("You may only change the COLOR and" 397710651e7b4f209a03649b9eac62facbc7bf864d17Michael Chan + " DATA columns for an existing Colors entry."); 39782f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 39798d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return handleUpdateColors(values, appendAccountToSelection(uri, selection, 39808d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE), 39812f251c778c06d21ed7693a70f4a1268ff929242eRoboErik selectionArgs); 39822f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 398343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio case CALENDARS: 39849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case CALENDARS_ID: 39859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 398643b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio long id; 398743b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (match == CALENDARS_ID) { 398843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = ContentUris.parseId(uri); 398943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 399043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // TODO: for supporting other sync adapters, we will need to 399143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // be able to deal with the following cases: 399243b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 1) selection to "_id=?" and pass in a selectionArgs 399343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 2) selection to "_id IN (1, 2, 3)" 399443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // 3) selection to "delete=0 AND _id=1" 39954cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik if (selection != null && TextUtils.equals(selection,"_id=?")) { 39964cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik id = Long.parseLong(selectionArgs[0]); 39974cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik } else if (selection != null && selection.startsWith("_id=")) { 399843b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // The ContentProviderOperation generates an _id=n string instead of 399943b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio // adding the id to the URL, so parse that out here. 400043b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio id = Long.parseLong(selection.substring(4)); 400143b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } else { 4002b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDARS, values, selection, selectionArgs); 400343b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 400443b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio } 400543b3eba05ef67bdd4b0a2b285b6ed2b377c136c5Fabrice Di Meglio if (!callerIsSyncAdapter) { 4006c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik values.put(Calendars.DIRTY, 1); 40077a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert addMutator(values, Calendars.MUTATORS); 40087a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } else { 40097a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (values.containsKey(Calendars.DIRTY) 40107a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert && values.getAsInteger(Calendars.DIRTY) == 0) { 40117a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert values.put(Calendars.MUTATORS, (String) null); 40127a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 40132fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff } 40149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS); 40159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (syncEvents != null) { 40169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff modifyCalendarSubscription(id, syncEvents == 1); 40179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4018387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik String color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY); 40192f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!TextUtils.isEmpty(color_id)) { 40202f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountName = values.getAsString(Calendars.ACCOUNT_NAME); 40212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik String accountType = values.getAsString(Calendars.ACCOUNT_TYPE); 40222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 40232f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Account account = getAccount(id); 40242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (account != null) { 40252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountName = account.name; 40262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik accountType = account.type; 40272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 40282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 40292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik verifyColorExists(accountName, accountType, color_id, Colors.TYPE_CALENDAR); 40302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 40319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4032b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID, 4033636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 40349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40353ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang if (result > 0) { 4036d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // if visibility was toggled, we need to update alarms 40374067700dbedcf4c8a379c9ecba9b5603972b4607Andy McFadden if (values.containsKey(Calendars.VISIBLE)) { 4038d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // pass false for removeAlarms since the call to 4039d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // scheduleNextAlarmLocked will remove any alarms for 4040d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // non-visible events anyways. removeScheduledAlarmsLocked 4041d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang // does not actually have the effect we want 4042ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false); 4043d74f8960b33b91b397c561662f69d8cd2e15ab20Mason Tang } 40443ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // update the widget 4045dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(callerIsSyncAdapter); 40463ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang } 40473ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 40489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return result; 40499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40507e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff case EVENTS: 40519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff case EVENTS_ID: 40529f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff { 4053d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden Cursor events = null; 40549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4055d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Grab the full set of columns for each selected event. 4056d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // TODO: define a projection with just the data we need (e.g. we don't need to 4057d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // validate the SYNC_* columns) 40589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4059d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden try { 4060d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (match == EVENTS_ID) { 4061d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // Single event, identified by ID. 4062d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden long id = ContentUris.parseId(uri); 4063d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 4064d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden SQL_WHERE_ID, new String[] { String.valueOf(id) }, 4065d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 40669ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } else { 4067d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden // One or more events, identified by the selection / selectionArgs. 4068d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events = mDb.query(Tables.EVENTS, null /* columns */, 4069d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden selection, selectionArgs, 4070d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden null /* groupBy */, null /* having */, null /* sortOrder */); 40719ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 407206c305d35741db303bd3aacd0eab5af8de0ab34eErik 4073d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events.getCount() == 0) { 407424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden Log.i(TAG, "No events to update: uri=" + uri + " selection=" + selection + 4075d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden " selectionArgs=" + Arrays.toString(selectionArgs)); 4076d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return 0; 4077d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } 40783ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 4079d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden return handleUpdateEvents(events, values, callerIsSyncAdapter); 4080d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden } finally { 4081d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden if (events != null) { 4082d5be35c42732d610040a64dd5bab782ef10014abAndy McFadden events.close(); 4083fc30eb24b8ed12dec09957479f489f67cc43b42bFabrice Di Meglio } 40849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 40859f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 408624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES: 408724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, false, values, selection, 408824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 408924abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case ATTENDEES_ID: 409024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.ATTENDEES, true, values, null, null, 409124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 40929f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 40932fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS_ID: { 40942fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 40952fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 40969f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff long id = ContentUris.parseId(uri); 4097b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, SQL_WHERE_ID, 4098636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff new String[] {String.valueOf(id)}); 40999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 41002fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case CALENDAR_ALERTS: { 41012fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // Note: dirty bit is not set for Alerts because it is not synced. 41022fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff // It is generated from Reminders, which is synced. 4103b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio return mDb.update(Tables.CALENDAR_ALERTS, values, selection, selectionArgs); 41049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 410524abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 410624abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case REMINDERS: 410724abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.REMINDERS, false, values, selection, 410824abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden selectionArgs, callerIsSyncAdapter); 41092fb3bc8f9ded55c0e379e1eaed2e036a5670b63aKen Shirriff case REMINDERS_ID: { 411024abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden int count = updateEventRelatedTable(uri, Tables.REMINDERS, true, values, null, null, 411124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden callerIsSyncAdapter); 41127e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff 41139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reschedule the event alarms because the 41149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // "minutes" field may have changed. 41159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (Log.isLoggable(TAG, Log.DEBUG)) { 41169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Log.d(TAG, "updateInternal() changing reminder"); 41179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 4118ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(false /* do not remove alarms */); 41197e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff return count; 41209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 412124abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden 412224abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden case EXTENDED_PROPERTIES_ID: 412324abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden return updateEventRelatedTable(uri, Tables.EXTENDED_PROPERTIES, true, values, 412424abf95cb69e8040af1a8dc2faace1bfb3ab505cAndy McFadden null, null, callerIsSyncAdapter); 412583512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff case SCHEDULE_ALARM_REMOVE: { 4126ea1b82d2ab660a15659258da19fabe19e5d4fbd5Tony Mak mCalendarAlarm.checkNextAlarm(true); 412783512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff return 0; 412883512315d187baad2c9dc3ed686cc23676c9f463Ken Shirriff } 41299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4130315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio case PROVIDER_PROPERTIES: { 4131315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!selection.equals("key=?")) { 4132315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Selection should be key=? for " + uri); 4133315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4134315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4135315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio List<String> list = Arrays.asList(selectionArgs); 4136315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4137315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { 4138315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio throw new UnsupportedOperationException("Invalid selection key: " + 4139315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS + " for " + uri); 4140315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4141315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4142315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Before it may be changed, save current Instances timezone for later use 4143315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstancesBeforeUpdate = mCalendarCache.readTimezoneInstances(); 4144315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4145315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the database with the provided values (this call may change the value 4146315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // of timezone Instances) 4147b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio int result = mDb.update(Tables.CALENDAR_CACHE, values, selection, selectionArgs); 4148315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4149315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if successful, do some house cleaning: 4150f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "home", set the Instances 4151f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the previous 4152f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone type is set to "auto", set the Instances 4153f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone to the current 4154f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // device one 4155f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // if the timezone Instances is set AND if we are in "home" 4156f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // timezone type, then save the timezone Instance into 4157f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik // "previous" too 4158315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (result > 0) { 4159315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone type... 4160315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (list.contains(CalendarCache.KEY_TIMEZONE_TYPE)) { 4161315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String value = values.getAsString(CalendarCache.COLUMN_NAME_VALUE); 4162315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value != null) { 4163315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "home" 4164315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (value.equals(CalendarCache.TIMEZONE_TYPE_HOME)) { 4165315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String previousTimezone = 4166315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.readTimezoneInstancesPrevious(); 4167315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (previousTimezone != null) { 4168315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(previousTimezone); 4169315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4170315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Regenerate Instances if the "home" timezone has changed 4171d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and notify widgets 4172315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(previousTimezone) ) { 4173315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4174d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4175315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4176315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4177315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are setting timezone type to "auto" 4178315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (value.equals(CalendarCache.TIMEZONE_TYPE_AUTO)) { 4179315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String localTimezone = TimeZone.getDefault().getID(); 4180315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstances(localTimezone); 4181315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (!timezoneInstancesBeforeUpdate.equals(localTimezone)) { 4182315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4183d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4184315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4185315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4186315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4187315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4188315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // If we are changing timezone Instances... 4189315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio else if (list.contains(CalendarCache.KEY_TIMEZONE_INSTANCES)) { 4190315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // if we are in "home" timezone type... 4191315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (isHomeTimezone()) { 4192315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio String timezoneInstances = mCalendarCache.readTimezoneInstances(); 4193315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Update the previous value 4194315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio mCalendarCache.writeTimezoneInstancesPrevious(timezoneInstances); 4195315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // Recompute Instances if the "home" timezone has changed 4196d8223536b8f050ff81dfb19a6ad6b186b3941211Erik // and send notifications to any widgets 4197315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio if (timezoneInstancesBeforeUpdate != null && 4198315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio !timezoneInstancesBeforeUpdate.equals(timezoneInstances)) { 4199315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio regenerateInstancesTable(); 4200d8223536b8f050ff81dfb19a6ad6b186b3941211Erik sendUpdateNotification(callerIsSyncAdapter); 4201315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4202315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4203315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4204315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4205315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio return result; 4206315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio } 4207315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 42089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff default: 42099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff throw new IllegalArgumentException("Unknown URL " + uri); 42109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42132f251c778c06d21ed7693a70f4a1268ff929242eRoboErik /** 42142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * Verifies that a color with the given index exists for the given Calendar 42152f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * entry. 42162f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * 42172f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountName The email of the account the color is for 42182f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @param accountType The type of account the color is for 42194755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorIndex The color_index being set for the calendar 42204755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan * @param colorType The type of color expected (Calendar/Event) 42212f251c778c06d21ed7693a70f4a1268ff929242eRoboErik * @return The color specified by the index 42222f251c778c06d21ed7693a70f4a1268ff929242eRoboErik */ 42234755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan private int verifyColorExists(String accountName, String accountType, String colorIndex, 42244755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan int colorType) { 42252f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 42262f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Cannot set color. A valid account does" 42272f251c778c06d21ed7693a70f4a1268ff929242eRoboErik + " not exist for this calendar."); 42282f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 42292f251c778c06d21ed7693a70f4a1268ff929242eRoboErik int color; 42302f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 42312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik try { 42324755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan c = getColorByTypeIndex(accountName, accountType, colorType, colorIndex); 42334755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan if (!c.moveToFirst()) { 42344755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan throw new IllegalArgumentException("Color type: " + colorType + " and index " 42354755452ab84f704f8ce4d7e0bf61a9faeeee2b99Michael Chan + colorIndex + " does not exist for account."); 42362f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 42372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik color = c.getInt(COLORS_COLOR_INDEX); 42382f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } finally { 42392f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 42402f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 42412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 42422f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 42432f251c778c06d21ed7693a70f4a1268ff929242eRoboErik return color; 42442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 42452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 42469ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendLastSyncedColumnToSelection(String selection, Uri uri) { 42479ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (getIsCallerSyncAdapter(uri)) { 42489ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return selection; 4249595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 42509ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert final StringBuilder sb = new StringBuilder(); 4251b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sb.append(CalendarContract.Events.LAST_SYNCED).append(" = 0"); 42529ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return appendSelection(sb, selection); 4253595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff } 4254595242cb01dc0d2d90b01613ff195b2be7b2559eKen Shirriff 42558d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert private String appendAccountToSelection( 42568d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert Uri uri, 42578d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String selection, 42588d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String accountNameColumn, 42598d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert String accountTypeColumn) { 42600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountName = QueryParameterUtils.getQueryParameter(uri, 4261b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_NAME); 42620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik final String accountType = QueryParameterUtils.getQueryParameter(uri, 4263b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.EventsEntity.ACCOUNT_TYPE); 42640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (!TextUtils.isEmpty(accountName)) { 42658d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert final StringBuilder sb = new StringBuilder() 42668d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(accountNameColumn) 42678d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append("=") 42688d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(DatabaseUtils.sqlEscapeString(accountName)) 42698d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(" AND ") 42708d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(accountTypeColumn) 42718d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append("=") 42728d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert .append(DatabaseUtils.sqlEscapeString(accountType)); 42738d2ed3bf1ef3525c3a6eb17b57f07b0af35ef4d0Alon Albert return appendSelection(sb, selection); 42749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } else { 42759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return selection; 42769f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42779f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 42789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 42799ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert private String appendSelection(StringBuilder sb, String selection) { 42809ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert if (!TextUtils.isEmpty(selection)) { 42819ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(" AND ("); 42829ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(selection); 42839ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sb.append(')'); 42849ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 42859ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert return sb.toString(); 42869ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert } 42879ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert 42880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik /** 42890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * Verifies that the operation is allowed and throws an exception if it 42900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * isn't. This defines the limits of a sync adapter call vs an app call. 4291683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * <p> 4292683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * Also rejects calls that have a selection but shouldn't, or that don't have a selection 4293683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden * but should. 4294c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik * 42950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param type The type of call, {@link #TRANSACTION_QUERY}, 42960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_INSERT}, {@link #TRANSACTION_UPDATE}, or 42970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * {@link #TRANSACTION_DELETE} 42980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param uri 42990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param values 43000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik * @param isSyncAdapter 43010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik */ 43020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyTransactionAllowed(int type, Uri uri, ContentValues values, 43030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik boolean isSyncAdapter, int uriMatch, String selection, String[] selectionArgs) { 4304f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Queries are never restricted to app- or sync-adapter-only, and we don't 4305f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // restrict the set of columns that may be accessed. 4306f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden if (type == TRANSACTION_QUERY) { 4307f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden return; 4308f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4309f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 4310683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (type == TRANSACTION_UPDATE || type == TRANSACTION_DELETE) { 43112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // TODO review this list, document in contract. 4312683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden if (!TextUtils.isEmpty(selection)) { 4313683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Only allow selections for the URIs that can reasonably use them. 43142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Whitelist of URIs allowed selections 4315683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4316683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case SYNCSTATE: 4317683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDARS: 4318683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4319683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4320683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case CALENDAR_ALERTS: 4321683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4322683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EXTENDED_PROPERTIES: 4323683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 43242f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 4325683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4326683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4327683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection not permitted for " + uri); 4328683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4329683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } else { 4330683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden // Disallow empty selections for some URIs. 43312f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // Blacklist of URIs _not_ allowed empty selections 4332683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden switch (uriMatch) { 4333683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case EVENTS: 4334683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case ATTENDEES: 4335683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case REMINDERS: 4336683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden case PROVIDER_PROPERTIES: 4337683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden throw new IllegalArgumentException("Selection must be specified for " 4338683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden + uri); 4339683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden default: 4340683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden break; 4341683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4342683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4343683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden } 4344683c9db3f0a244969037a7f20767a35b3187ca4bAndy McFadden 4345f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden // Only the sync adapter can use these to make changes. 43462f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (!isSyncAdapter) { 43472f251c778c06d21ed7693a70f4a1268ff929242eRoboErik switch (uriMatch) { 43482f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE: 43492f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case SYNCSTATE_ID: 43502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES: 43512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case EXTENDED_PROPERTIES_ID: 43522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik case COLORS: 43532f251c778c06d21ed7693a70f4a1268ff929242eRoboErik throw new IllegalArgumentException("Only sync adapters may write using " + uri); 43542f251c778c06d21ed7693a70f4a1268ff929242eRoboErik default: 43552f251c778c06d21ed7693a70f4a1268ff929242eRoboErik break; 4356f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4357f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden } 4358f07b66b00b0ee35bddc64a6f7ac4039627fbcf89Andy McFadden 43590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (type) { 43600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_INSERT: 43610739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 43620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException( 43630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Inserting into instances not supported"); 43640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4365c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4366c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 43670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 43680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 43690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 43700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 43710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 43720739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 43730739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43740739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43750739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_UPDATE: 43760739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 43770739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Updating instances not supported"); 43780739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 4379c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik // Check there are no columns restricted to the provider 4380c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik verifyColumns(values, uriMatch); 43810739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 43820739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 43830739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 43840739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } else { 43850739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that sync only columns aren't included 43860739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyNoSyncColumns(values, uriMatch); 43870739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43880739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43890739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case TRANSACTION_DELETE: 43900739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (uriMatch == INSTANCES) { 43910739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new UnsupportedOperationException("Deleting instances not supported"); 43920739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (isSyncAdapter) { 43940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // check that account and account type are specified 43950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik verifyHasAccount(uri, selection, selectionArgs); 43960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 43980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 43990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44000739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 44010739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyHasAccount(Uri uri, String selection, String[] selectionArgs) { 4402c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String accountName = QueryParameterUtils.getQueryParameter(uri, Calendars.ACCOUNT_NAME); 44030739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String accountType = QueryParameterUtils.getQueryParameter(uri, 4404c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Calendars.ACCOUNT_TYPE); 44050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 44060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (selection != null && selection.startsWith(ACCOUNT_SELECTION_PREFIX)) { 44070739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountName = selectionArgs[0]; 44080739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik accountType = selectionArgs[1]; 44090739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44100739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44110739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { 44120739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException( 44130739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik "Sync adapters must specify an account and account type: " + uri); 44140739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44150739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44160739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 4417c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private void verifyColumns(ContentValues values, int uriMatch) { 4418c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 4419c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik return; 4420c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4421c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik String[] columns; 4422c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik switch (uriMatch) { 4423c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4424c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4425c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4426c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4427c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = Events.PROVIDER_WRITABLE_COLUMNS; 4428c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4429c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik default: 4430c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik columns = PROVIDER_WRITABLE_DEFAULT_COLUMNS; 4431c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4432c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4433c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 4434c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik for (int i = 0; i < columns.length; i++) { 4435c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values.containsKey(columns[i])) { 4436c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik throw new IllegalArgumentException("Only the provider may write to " + columns[i]); 4437c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4438c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4439c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik } 4440c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik 44410739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private void verifyNoSyncColumns(ContentValues values, int uriMatch) { 4442c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik if (values == null || values.size() == 0) { 44430739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik return; 44440739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44450739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik String[] syncColumns; 44460739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik switch (uriMatch) { 44470739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS: 44480739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDARS_ID: 44490739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES: 44500739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik case CALENDAR_ENTITIES_ID: 4451c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Calendars.SYNC_WRITABLE_COLUMNS; 4452c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik break; 4453c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS: 4454c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENTS_ID: 4455c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES: 4456c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik case EVENT_ENTITIES_ID: 4457c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik syncColumns = Events.SYNC_WRITABLE_COLUMNS; 44580739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 44590739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik default: 44600739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik syncColumns = SYNC_WRITABLE_DEFAULT_COLUMNS; 44610739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik break; 44620739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 44630739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44640739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik for (int i = 0; i < syncColumns.length; i++) { 44650739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik if (values.containsKey(syncColumns[i])) { 44660739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik throw new IllegalArgumentException("Only sync adapters may write to " 44670739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik + syncColumns[i]); 44680739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44690739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44700739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik } 44710739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 44729f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private void modifyCalendarSubscription(long id, boolean syncEvents) { 44739f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // get the account, url, and current selected state 44749f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // for this calendar. 44759f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Cursor cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, id), 4476c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik new String[] {Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_TYPE, 4477fa332ecedc0c340109811552407142f6e4f600b2RoboErik Calendars.CAL_SYNC1, Calendars.SYNC_EVENTS}, 44789f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selection */, 44799f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* selectionArgs */, 44809f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff null /* sort */); 44819f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 44829f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account account = null; 44839f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String calendarUrl = null; 44849f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff boolean oldSyncEvents = false; 4485ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor != null) { 44869f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 4487ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff if (cursor.moveToFirst()) { 4488ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountName = cursor.getString(0); 4489ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff final String accountType = cursor.getString(1); 4490ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff account = new Account(accountName, accountType); 4491ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff calendarUrl = cursor.getString(2); 4492ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff oldSyncEvents = (cursor.getInt(3) != 0); 4493ae2599e63fe5e153ba735564ef3c0898d4f3c833Ken Shirriff } 44949f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 44952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (cursor != null) 44962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik cursor.close(); 44979f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44989f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 44999f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45009535627bf6295cd94447beb83e1aac41f50c3600Erik if (account == null) { 45019f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // should not happen? 4502f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.WARN)) { 4503f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.w(TAG, "Cannot update subscription because account " 4504f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio + "is empty -- should not happen."); 4505f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 45069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 45079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 45089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45099535627bf6295cd94447beb83e1aac41f50c3600Erik if (TextUtils.isEmpty(calendarUrl)) { 45109535627bf6295cd94447beb83e1aac41f50c3600Erik // Passing in a null Url will cause it to not add any extras 45119535627bf6295cd94447beb83e1aac41f50c3600Erik // Should only happen for non-google calendars. 45129535627bf6295cd94447beb83e1aac41f50c3600Erik calendarUrl = null; 45139535627bf6295cd94447beb83e1aac41f50c3600Erik } 45149535627bf6295cd94447beb83e1aac41f50c3600Erik 45159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (oldSyncEvents == syncEvents) { 45169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // nothing to do 45179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff return; 45189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 45199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 45209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // If the calendar is not selected for syncing, then don't download 45219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // events. 45229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.scheduleSync(account, !syncEvents, calendarUrl); 45239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 45249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4525a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4526a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4527a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4528a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. 4529dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang * 45309ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4531a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4532dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(boolean callerIsSyncAdapter) { 4533dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use -1 to represent an update to all events 4534dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(-1, callerIsSyncAdapter); 4535a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4536a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4537a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4538a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * Call this to trigger a broadcast of the ACTION_PROVIDER_CHANGED intent. 4539a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This also provides a timeout, so any calls to this method will be batched 4540a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * over a period of BROADCAST_TIMEOUT_MILLIS defined in this class. The 4541a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * actual sending of the intent is done in 4542a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * {@link #doSendUpdateNotification()}. 4543a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 4544a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * TODO add support for eventId 4545a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 45469ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param eventId the ID of the event that changed, or -1 for no specific event 45479ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @param callerIsSyncAdapter whether or not the update is being triggered by a sync 4548a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4549dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang private void sendUpdateNotification(long eventId, 4550dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang boolean callerIsSyncAdapter) { 4551a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Are there any pending broadcast requests? 4552a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang if (mBroadcastHandler.hasMessages(UPDATE_BROADCAST_MSG)) { 4553a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang // Delete any pending requests, before requeuing a fresh one 4554a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang mBroadcastHandler.removeMessages(UPDATE_BROADCAST_MSG); 4555a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } else { 4556dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Because the handler does not guarantee message delivery in 4557dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // the case that the provider is killed, we need to make sure 4558dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // that the provider stays alive long enough to deliver the 4559dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // notification. This empty service is sufficient to "wedge" the 4560dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // process until we stop it here. 4561dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mContext.startService(new Intent(mContext, EmptyService.class)); 4562dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang } 4563dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // We use a much longer delay for sync-related updates, to prevent any 4564dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // receivers from slowing down the sync 4565dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang long delay = callerIsSyncAdapter ? 4566dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang SYNC_UPDATE_BROADCAST_TIMEOUT_MILLIS : 4567dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang UPDATE_BROADCAST_TIMEOUT_MILLIS; 4568dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // Despite the fact that we actually only ever use one message at a time 4569dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // for now, it is really important to call obtainMessage() to get a 4570dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // clean instance. This avoids potentially infinite loops resulting 4571dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // adding the same instance to the message queue twice, since the 4572dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang // message queue implements its linked list using a field from Message. 4573a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Message msg = mBroadcastHandler.obtainMessage(UPDATE_BROADCAST_MSG); 4574dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang mBroadcastHandler.sendMessageDelayed(msg, delay); 4575a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4576a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 4577a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang /** 4578a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * This method should not ever be called directly, to prevent sending too 4579a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * many potentially expensive broadcasts. Instead, call 45809ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * {@link #sendUpdateNotification(boolean)} instead. 4581a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang * 45829ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert * @see #sendUpdateNotification(boolean) 4583a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang */ 4584a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang private void doSendUpdateNotification() { 4585a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang Intent intent = new Intent(Intent.ACTION_PROVIDER_CHANGED, 4586b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.CONTENT_URI); 4587f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.INFO)) { 4588f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.i(TAG, "Sending notification intent: " + intent); 4589f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 4590e7a04f1fe637bc1322a6b4942e0251e3831cd544Fabrice Di Meglio mContext.sendBroadcast(intent, null); 4591a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang } 4592a84cc39ca05e0e799f03e04a1d3e30b5ff733cd7Mason Tang 45930739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_QUERY = 0; 45940739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_INSERT = 1; 45950739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_UPDATE = 2; 45960739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final int TRANSACTION_DELETE = 3; 45970739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 45980739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:off 45990739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik private static final String[] SYNC_WRITABLE_DEFAULT_COLUMNS = new String[] { 4600b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars.DIRTY, 4601b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.Calendars._SYNC_ID 46020739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik }; 4603c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik private static final String[] PROVIDER_WRITABLE_DEFAULT_COLUMNS = new String[] { 4604c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik }; 46050739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik // @formatter:on 46060739be04415dfd61619b5611e82b7c9a6c83eae3RoboErik 46079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS = 1; 46089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int EVENTS_ID = 2; 46099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final int INSTANCES = 3; 46102ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS = 4; 46112ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDARS_ID = 5; 46122ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES = 6; 46132ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int ATTENDEES_ID = 7; 46142ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS = 8; 46152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int REMINDERS_ID = 9; 46162ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES = 10; 46172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EXTENDED_PROPERTIES_ID = 11; 46182ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS = 12; 46192ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_ID = 13; 46202ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ALERTS_BY_INSTANCE = 14; 46212ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_BY_DAY = 15; 46222ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE = 16; 46232ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SYNCSTATE_ID = 17; 46242ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES = 18; 46252ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_ENTITIES_ID = 19; 46262ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int EVENT_DAYS = 20; 46272ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int SCHEDULE_ALARM_REMOVE = 22; 46282ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int TIME = 23; 46292ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES = 24; 46302ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int CALENDAR_ENTITIES_ID = 25; 46312ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH = 26; 46322ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int INSTANCES_SEARCH_BY_DAY = 27; 46332ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik private static final int PROVIDER_PROPERTIES = 28; 4634bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID = 29; 4635bcba82631ab0ee16efe58f0e0b0b9c18d93a6fd2Andy McFadden private static final int EXCEPTION_ID2 = 30; 46363b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden private static final int EMMA = 31; 46372f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final int COLORS = 32; 46389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 46399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 46409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sInstancesProjectionMap; 46412f251c778c06d21ed7693a70f4a1268ff929242eRoboErik private static final HashMap<String, String> sColorsProjectionMap; 4642b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert protected static final HashMap<String, String> sCalendarsProjectionMap; 4643f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik protected static final HashMap<String, String> sEventsProjectionMap; 464419fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana private static final HashMap<String, String> sEventEntitiesProjectionMap; 46459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sAttendeesProjectionMap; 46469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sRemindersProjectionMap; 46479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff private static final HashMap<String, String> sCalendarAlertsProjectionMap; 4648315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio private static final HashMap<String, String> sCalendarCacheProjectionMap; 464939c65e5716e21e863d8de587d139dae85f99422fFred Quintana private static final HashMap<String, String> sCountProjectionMap; 46509f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 46519f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff static { 4652b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/when/*/*", INSTANCES); 4653b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/whenbyday/*/*", INSTANCES_BY_DAY); 4654b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/search/*/*/*", INSTANCES_SEARCH); 4655b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/searchbyday/*/*/*", 465681d904d66bd746c077cc0baa6cf1f51fe030eac4Mason Tang INSTANCES_SEARCH_BY_DAY); 4657b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "instances/groupbyday/*/*", EVENT_DAYS); 4658b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events", EVENTS); 4659b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "events/#", EVENTS_ID); 4660b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities", EVENT_ENTITIES); 4661b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "event_entities/#", EVENT_ENTITIES_ID); 4662b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars", CALENDARS); 4663b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendars/#", CALENDARS_ID); 4664b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities", CALENDAR_ENTITIES); 4665b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_entities/#", CALENDAR_ENTITIES_ID); 4666b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees", ATTENDEES); 4667b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "attendees/#", ATTENDEES_ID); 4668b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders", REMINDERS); 4669b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "reminders/#", REMINDERS_ID); 4670b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties", EXTENDED_PROPERTIES); 4671b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "extendedproperties/#", 4672b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik EXTENDED_PROPERTIES_ID); 4673b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts", CALENDAR_ALERTS); 4674b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/#", CALENDAR_ALERTS_ID); 4675b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "calendar_alerts/by_instance", 4676b57f228c23d3672c1f08153f3fcc88ced2011714Ken Shirriff CALENDAR_ALERTS_BY_INSTANCE); 4677b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate", SYNCSTATE); 4678b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 4679b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, 4680b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarAlarmManager.SCHEDULE_ALARM_REMOVE_PATH, SCHEDULE_ALARM_REMOVE); 4681b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time/#", TIME); 4682b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "time", TIME); 4683b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "properties", PROVIDER_PROPERTIES); 4684b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID); 4685b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2); 46863b7c1cc29240628ed0f61b26375eba21665fe6d7Andy McFadden sUriMatcher.addURI(CalendarContract.AUTHORITY, "emma", EMMA); 46872f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sUriMatcher.addURI(CalendarContract.AUTHORITY, "colors", COLORS); 46889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 468939c65e5716e21e863d8de587d139dae85f99422fFred Quintana /** Contains just BaseColumns._COUNT */ 469039c65e5716e21e863d8de587d139dae85f99422fFred Quintana sCountProjectionMap = new HashMap<String, String>(); 469108007188ce3af03ed13e9252febc9b22e1bfe953Keith Cheung sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*) AS " + BaseColumns._COUNT); 469239c65e5716e21e863d8de587d139dae85f99422fFred Quintana 46932f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap = new HashMap<String, String>(); 46942f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors._ID, Colors._ID); 46952f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.DATA, Colors.DATA); 46962f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_NAME, Colors.ACCOUNT_NAME); 46972f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.ACCOUNT_TYPE, Colors.ACCOUNT_TYPE); 4698387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sColorsProjectionMap.put(Colors.COLOR_KEY, Colors.COLOR_KEY); 46992f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR_TYPE, Colors.COLOR_TYPE); 47002f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sColorsProjectionMap.put(Colors.COLOR, Colors.COLOR); 47012f251c778c06d21ed7693a70f4a1268ff929242eRoboErik 4702b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap = new HashMap<String, String>(); 4703b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars._ID, Calendars._ID); 4704b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.ACCOUNT_NAME, Calendars.ACCOUNT_NAME); 4705b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.ACCOUNT_TYPE, Calendars.ACCOUNT_TYPE); 4706b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars._SYNC_ID, Calendars._SYNC_ID); 4707b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.DIRTY, Calendars.DIRTY); 47087a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert sCalendarsProjectionMap.put(Calendars.MUTATORS, Calendars.MUTATORS); 4709b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.NAME, Calendars.NAME); 4710b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put( 4711b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 4712b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 4713b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR_KEY); 4714b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, 4715b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert Calendars.CALENDAR_ACCESS_LEVEL); 4716b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 4717b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.SYNC_EVENTS, Calendars.SYNC_EVENTS); 4718b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CALENDAR_LOCATION, Calendars.CALENDAR_LOCATION); 4719b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 4720b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 4721b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.IS_PRIMARY, 4722b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert "COALESCE(" + Events.IS_PRIMARY + ", " 472308007188ce3af03ed13e9252febc9b22e1bfe953Keith Cheung + Calendars.OWNER_ACCOUNT + " = " + Calendars.ACCOUNT_NAME + ") AS " 472408007188ce3af03ed13e9252febc9b22e1bfe953Keith Cheung + Calendars.IS_PRIMARY); 4725b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, 4726b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert Calendars.CAN_ORGANIZER_RESPOND); 4727b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 4728b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAN_PARTIALLY_UPDATE, Calendars.CAN_PARTIALLY_UPDATE); 4729b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 4730b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 4731b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.ALLOWED_AVAILABILITY, Calendars.ALLOWED_AVAILABILITY); 4732b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.ALLOWED_ATTENDEE_TYPES, 4733b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert Calendars.ALLOWED_ATTENDEE_TYPES); 4734b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.DELETED, Calendars.DELETED); 4735b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 4736b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 4737b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 4738b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 4739b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 4740b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 4741b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 4742b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 4743b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 4744b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sCalendarsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 4745b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert 47469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sEventsProjectionMap = new HashMap<String, String>(); 47479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Events columns 474802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME); 474902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.ACCOUNT_TYPE, Events.ACCOUNT_TYPE); 4750c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.TITLE, Events.TITLE); 4751c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4752c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4753c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.STATUS, Events.STATUS); 475402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4755387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Events.EVENT_COLOR_KEY, Events.EVENT_COLOR_KEY); 4756c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4757c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART); 4758c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DTEND, Events.DTEND); 4759c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4760c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4761c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DURATION, Events.DURATION); 4762c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4763c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4764c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4765c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4766c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, Events.HAS_EXTENDED_PROPERTIES); 4767c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RRULE, Events.RRULE); 4768c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.RDATE, Events.RDATE); 4769c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXRULE, Events.EXRULE); 4770c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.EXDATE, Events.EXDATE); 4771c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 477234c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4773c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, Events.ORIGINAL_INSTANCE_TIME); 4774c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4775c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4776c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4777c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4778c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, Events.GUESTS_CAN_INVITE_OTHERS); 4779c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4780c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4781c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4782b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sEventsProjectionMap.put(Events.IS_ORGANIZER, Events.IS_ORGANIZER); 4783c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventsProjectionMap.put(Events.CUSTOM_APP_PACKAGE, Events.CUSTOM_APP_PACKAGE); 4784c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventsProjectionMap.put(Events.CUSTOM_APP_URI, Events.CUSTOM_APP_URI); 4785501e60bcb1b519d80723f7b64ba60bd079b8ec8dSara Ting sEventsProjectionMap.put(Events.UID_2445, Events.UID_2445); 4786c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DELETED, Events.DELETED); 478702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 47889f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4789e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // Put the shared items into the Attendees, Reminders projection map 47901ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sAttendeesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 47911ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff sRemindersProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 47921ae4c22f15c107cd9f9cd8babaa11005e45e4647Ken Shirriff 47939f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Calendar columns 4794c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR); 4795387535fec9f646e0b7acb82d5354f2b5ebee4395RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR_KEY); 479602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL); 4797c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE); 479802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE); 4799c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT); 480002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME); 480102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS); 48022f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap 48032f251c778c06d21ed7693a70f4a1268ff929242eRoboErik .put(Calendars.ALLOWED_ATTENDEE_TYPES, Calendars.ALLOWED_ATTENDEE_TYPES); 48042f251c778c06d21ed7693a70f4a1268ff929242eRoboErik sEventsProjectionMap.put(Calendars.ALLOWED_AVAILABILITY, Calendars.ALLOWED_AVAILABILITY); 480502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS); 480602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND); 480702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE); 4808c339afc7df041ebfc5f4587f78cf38562aa23459Alon Albert sEventsProjectionMap.put(Events.DISPLAY_COLOR, Events.DISPLAY_COLOR); 48099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 4810982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff // Put the shared items into the Instances projection map 4811e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // The Instances and CalendarAlerts are joined with Calendars, so the projections include 4812e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff // the above Calendar columns. 4813982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff sInstancesProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4814e74157e34e174c923032a4b93ad298d0f234879cKen Shirriff sCalendarAlertsProjectionMap = new HashMap<String, String>(sEventsProjectionMap); 4815982cbfe8b4e5af45b06fc5c18ff9e0868378ee40Ken Shirriff 4816c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events._ID, Events._ID); 481702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 481802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 481902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 482002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 482102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 482202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 48239ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 482402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 482502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 482602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 482702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 482802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 482902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 483002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 483102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 483202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 483302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 483402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 483502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 483602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 4837c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY); 48387a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert sEventsProjectionMap.put(Events.MUTATORS, Events.MUTATORS); 48399ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 48409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 484146f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff sEventEntitiesProjectionMap = new HashMap<String, String>(); 4842c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.TITLE, Events.TITLE); 4843c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_LOCATION, Events.EVENT_LOCATION); 4844c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION); 4845c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.STATUS, Events.STATUS); 484602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR); 4847ccfee5ca255652892b407fa046a797bf62d3b1c3Alon Albert sEventEntitiesProjectionMap.put(Events.EVENT_COLOR_KEY, Events.EVENT_COLOR_KEY); 4848c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS); 4849c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTSTART, Events.DTSTART); 4850c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DTEND, Events.DTEND); 4851c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_TIMEZONE, Events.EVENT_TIMEZONE); 4852c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EVENT_END_TIMEZONE, Events.EVENT_END_TIMEZONE); 4853c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DURATION, Events.DURATION); 4854c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ALL_DAY, Events.ALL_DAY); 4855c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ACCESS_LEVEL, Events.ACCESS_LEVEL); 4856c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.AVAILABILITY, Events.AVAILABILITY); 4857c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ALARM, Events.HAS_ALARM); 4858c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_EXTENDED_PROPERTIES, 4859c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.HAS_EXTENDED_PROPERTIES); 4860c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RRULE, Events.RRULE); 4861c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.RDATE, Events.RDATE); 4862c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXRULE, Events.EXRULE); 4863c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.EXDATE, Events.EXDATE); 4864c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_SYNC_ID, Events.ORIGINAL_SYNC_ID); 486534c32cd924eb8ee28381106b37044b78fd8cbc30RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ID, Events.ORIGINAL_ID); 4866c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_INSTANCE_TIME, 4867c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.ORIGINAL_INSTANCE_TIME); 4868c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORIGINAL_ALL_DAY, Events.ORIGINAL_ALL_DAY); 4869c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.LAST_DATE, Events.LAST_DATE); 4870c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.HAS_ATTENDEE_DATA, Events.HAS_ATTENDEE_DATA); 4871c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.CALENDAR_ID, Events.CALENDAR_ID); 4872c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_INVITE_OTHERS, 4873c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik Events.GUESTS_CAN_INVITE_OTHERS); 4874c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_MODIFY, Events.GUESTS_CAN_MODIFY); 4875c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.GUESTS_CAN_SEE_GUESTS, Events.GUESTS_CAN_SEE_GUESTS); 4876c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.ORGANIZER, Events.ORGANIZER); 4877b2695bf3cfb173c4c5ba7bfd3c93ba8a51d65810Alon Albert sEventEntitiesProjectionMap.put(Events.IS_ORGANIZER, Events.IS_ORGANIZER); 4878c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventEntitiesProjectionMap.put(Events.CUSTOM_APP_PACKAGE, Events.CUSTOM_APP_PACKAGE); 4879c81732aeadada8f8bc4c216a317ba458374af2c9Michael Chan sEventEntitiesProjectionMap.put(Events.CUSTOM_APP_URI, Events.CUSTOM_APP_URI); 4880501e60bcb1b519d80723f7b64ba60bd079b8ec8dSara Ting sEventEntitiesProjectionMap.put(Events.UID_2445, Events.UID_2445); 4881c8383567db3ade2aea28447ad3bd09ac3033bcd7RoboErik sEventEntitiesProjectionMap.put(Events.DELETED, Events.DELETED); 488219fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._ID, Events._ID); 488319fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana sEventEntitiesProjectionMap.put(Events._SYNC_ID, Events._SYNC_ID); 488402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA1, Events.SYNC_DATA1); 488502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA2, Events.SYNC_DATA2); 488602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA3, Events.SYNC_DATA3); 488702f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA4, Events.SYNC_DATA4); 488802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA5, Events.SYNC_DATA5); 488902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA6, Events.SYNC_DATA6); 48909ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.SYNC_DATA7, Events.SYNC_DATA7); 489102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA8, Events.SYNC_DATA8); 489202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9); 489302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10); 4894470aa5bc291ca33d51dda356f38ac2954026da9aAlon Albert sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY); 48957a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert sEventEntitiesProjectionMap.put(Events.MUTATORS, Events.MUTATORS); 48969ec70fada3d8f7cf56d6b0d0947823ec5bce572cAlon Albert sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED); 4897fa332ecedc0c340109811552407142f6e4f600b2RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1); 489802f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2); 489902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC3, Calendars.CAL_SYNC3); 490002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC4, Calendars.CAL_SYNC4); 490102f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC5, Calendars.CAL_SYNC5); 490202f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC6, Calendars.CAL_SYNC6); 490302f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC7, Calendars.CAL_SYNC7); 490402f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC8, Calendars.CAL_SYNC8); 490502f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9); 490602f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10); 490719fb3af2ec12621bca575f5518c2ba3831cb3600Fred Quintana 49089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Instances columns 49091b6beb61ef04c3da6ab0bdf8504ffecea2b9534cFabrice Di Meglio sInstancesProjectionMap.put(Events.DELETED, "Events.deleted as deleted"); 49109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.BEGIN, "begin"); 49119f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END, "end"); 49129f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.EVENT_ID, "Instances.event_id AS event_id"); 49139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances._ID, "Instances._id AS _id"); 49149f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_DAY, "startDay"); 49159f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_DAY, "endDay"); 49169f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.START_MINUTE, "startMinute"); 49179f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sInstancesProjectionMap.put(Instances.END_MINUTE, "endMinute"); 49189f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 49199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Attendees columns 49209f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.EVENT_ID, "event_id"); 49219f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees._ID, "Attendees._id AS _id"); 49229f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_NAME, "attendeeName"); 49239f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_EMAIL, "attendeeEmail"); 49249f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_STATUS, "attendeeStatus"); 49259f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_RELATIONSHIP, "attendeeRelationship"); 49269f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sAttendeesProjectionMap.put(Attendees.ATTENDEE_TYPE, "attendeeType"); 4927bafe9de156292f65b1079dd1eb586669f573d9e6Michael Chan sAttendeesProjectionMap.put(Attendees.ATTENDEE_IDENTITY, "attendeeIdentity"); 4928bafe9de156292f65b1079dd1eb586669f573d9e6Michael Chan sAttendeesProjectionMap.put(Attendees.ATTENDEE_ID_NAMESPACE, "attendeeIdNamespace"); 492902f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 493002f97c538fc46a08d857d2c807c76fd0eec12493RoboErik sAttendeesProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 49319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 49329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // Reminders columns 49339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.EVENT_ID, "event_id"); 49349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders._ID, "Reminders._id AS _id"); 49359f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.MINUTES, "minutes"); 49369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sRemindersProjectionMap.put(Reminders.METHOD, "method"); 4937361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events.DELETED, "Events.deleted AS deleted"); 4938361695206f7a25577ddc374f20868105cae531cdAndy McFadden sRemindersProjectionMap.put(Events._SYNC_ID, "Events._sync_id AS _sync_id"); 49399f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 49409f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // CalendarAlerts columns 49419f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.EVENT_ID, "event_id"); 49429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts._ID, "CalendarAlerts._id AS _id"); 49439f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.BEGIN, "begin"); 49449f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.END, "end"); 49459f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.ALARM_TIME, "alarmTime"); 4946c3f54281b3b4a1646923a726c9a1731bd39324a0Michael Chan sCalendarAlertsProjectionMap.put(CalendarAlerts.NOTIFY_TIME, "notifyTime"); 49479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.STATE, "state"); 49489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff sCalendarAlertsProjectionMap.put(CalendarAlerts.MINUTES, "minutes"); 4949315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio 4950315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio // CalendarCache columns 4951315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap = new HashMap<String, String>(); 4952315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_KEY, "key"); 4953315d9326acd39566959f3c547225483f1fb6aefcFabrice Di Meglio sCalendarCacheProjectionMap.put(CalendarCache.COLUMN_NAME_VALUE, "value"); 49549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 49559f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 495664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 49579f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff /** 495864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * This is called by AccountManager when the set of accounts is updated. 495964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * <p> 496064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * We are overriding this since we need to delete from the 49619f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff * Calendars table, which is not syncable, which has triggers that 49627e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * will delete from the Events and tables, which are 49637e3ec5f2025164fca508f81a5a01940bc912e064Ken Shirriff * syncable. TODO: update comment, make sure deletes don't get synced. 496464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * 496564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * @param accounts The list of currently active accounts. 49669f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff */ 4967f5930ab5c4c0fe728cb8fdf923d482b4f272eb1fRoboErik @Override 49689f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff public void onAccountsUpdated(Account[] accounts) { 496964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Thread thread = new AccountsUpdatedThread(accounts); 497064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden thread.start(); 497164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 497264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 497364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private class AccountsUpdatedThread extends Thread { 497464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private Account[] mAccounts; 497564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 497664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden AccountsUpdatedThread(Account[] accounts) { 497764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden mAccounts = accounts; 497864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 497964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 498064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden @Override 498164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden public void run() { 498264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // The process could be killed while the thread runs. Right now that isn't a problem, 498364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // because we'll just call removeStaleAccounts() again when the provider restarts, but 498464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // if we want to do additional actions we may need to use a service (e.g. start 498564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden // EmptyService in onAccountsUpdated() and stop it when we finish here). 498664af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 498764af00286ccc989f390f7f43153688d4173ac62dAndy McFadden Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 498864af00286ccc989f390f7f43153688d4173ac62dAndy McFadden removeStaleAccounts(mAccounts); 498964af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 499064af00286ccc989f390f7f43153688d4173ac62dAndy McFadden } 499164af00286ccc989f390f7f43153688d4173ac62dAndy McFadden 499264af00286ccc989f390f7f43153688d4173ac62dAndy McFadden /** 499364af00286ccc989f390f7f43153688d4173ac62dAndy McFadden * Makes sure there are no entries for accounts that no longer exist. 499464af00286ccc989f390f7f43153688d4173ac62dAndy McFadden */ 499564af00286ccc989f390f7f43153688d4173ac62dAndy McFadden private void removeStaleAccounts(Account[] accounts) { 49960ab307238107189b4717127b638e6c7dc9f988f1Jay Shrauner mDb = mDbHelper.getWritableDatabase(); 4997ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio if (mDb == null) { 4998ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio return; 4999ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio } 50009f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 500146f3f01b132f97b51ec1f4670769dda499cd9da5Ken Shirriff HashSet<Account> validAccounts = new HashSet<Account>(); 50029f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accounts) { 50039f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff validAccounts.add(new Account(account.name, account.type)); 50049f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50059f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff ArrayList<Account> accountsToDelete = new ArrayList<Account>(); 50069f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 50079f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.beginTransaction(); 50082f251c778c06d21ed7693a70f4a1268ff929242eRoboErik Cursor c = null; 50099f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff try { 50109f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 50112f251c778c06d21ed7693a70f4a1268ff929242eRoboErik for (String table : new String[]{Tables.CALENDARS, Tables.COLORS}) { 5012ae4e50b2c35fe4549d1df6568544aa72057dcbe1Fabrice Di Meglio // Find all the accounts the calendar DB knows about, mark the ones that aren't 50139f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff // in the valid set for deletion. 50142f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = mDb.rawQuery("SELECT DISTINCT " + 50152ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_NAME + 50167cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio "," + 50172ce7955da7dffec7819ed38be85e72df8a6f33dcRoboErik Calendars.ACCOUNT_TYPE + 50187cb72fa3ea680dce378d8dac71f878e52e03f83aFabrice Di Meglio " FROM " + table, null); 50199f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff while (c.moveToNext()) { 50204cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // ACCOUNT_TYPE_LOCAL is to store calendars not associated 50214cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // with a system account. Typically, a calendar must be 50224cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // associated with an account on the device or it will be 50234cd49582b08291d51cd152e1d2bff7fb547bcae2RoboErik // deleted. 5024b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik if (c.getString(0) != null 5025b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && c.getString(1) != null 5026b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik && !TextUtils.equals(c.getString(1), 5027b9644fe24edf9e25f0b21c1394e88d25070e0238RoboErik CalendarContract.ACCOUNT_TYPE_LOCAL)) { 50289f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff Account currAccount = new Account(c.getString(0), c.getString(1)); 50299f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff if (!validAccounts.contains(currAccount)) { 50309f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff accountsToDelete.add(currAccount); 50319f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50329f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50339f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50349f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff c.close(); 50352f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c = null; 50369f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50379f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 50389f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff for (Account account : accountsToDelete) { 5039f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 5040f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio Log.d(TAG, "removing data for removed account " + account); 5041f39a880f0554ce58ab7cf5e2e2191cb01a60fe75Fabrice Di Meglio } 50429f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff String[] params = new String[]{account.name, account.type}; 5043b5e628f741ca3f09f31af41cc12d3c8661caf330Fabrice Di Meglio mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params); 50442f251c778c06d21ed7693a70f4a1268ff929242eRoboErik // This will be a no-op for accounts without a color palette. 50452f251c778c06d21ed7693a70f4a1268ff929242eRoboErik mDb.execSQL(SQL_DELETE_FROM_COLORS, params); 50469f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50479f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); 50489f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.setTransactionSuccessful(); 50499f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } finally { 50502f251c778c06d21ed7693a70f4a1268ff929242eRoboErik if (c != null) { 50512f251c778c06d21ed7693a70f4a1268ff929242eRoboErik c.close(); 50522f251c778c06d21ed7693a70f4a1268ff929242eRoboErik } 50539f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff mDb.endTransaction(); 50549f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50553ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang 50563ee5e75440f52e76bfef1aae73e2a4047ae45e7cMason Tang // make sure the widget reflects the account changes 5057dabc4a9d60080bf97e50baad83acf2ec6c3adc07Mason Tang sendUpdateNotification(false); 50589f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff } 50599f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff 5060636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff /** 5061636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * Inserts an argument at the beginning of the selection arg list. 5062636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * 5063636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * The {@link android.database.sqlite.SQLiteQueryBuilder}'s where clause is 5064636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended to the user's where clause (combined with 'AND') to generate 5065636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * the final where close, so arguments associated with the QueryBuilder are 5066636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff * prepended before any user selection args to keep them in the right order. 5067636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff */ 5068636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff private String[] insertSelectionArg(String[] selectionArgs, String arg) { 5069636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff if (selectionArgs == null) { 5070636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return new String[] {arg}; 5071636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } else { 5072636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff int newLength = selectionArgs.length + 1; 5073636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff String[] newSelectionArgs = new String[newLength]; 5074636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff newSelectionArgs[0] = arg; 5075636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length); 5076636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff return newSelectionArgs; 5077636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 5078636b4fb283b9d2802afd179b20a24f2f5035ee69Ken Shirriff } 50797a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert 50807a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert private String getCallingPackageName() { 508196d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein if (getCachedCallingPackage() != null) { 508296d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein // If the calling package is null, use the best available as a fallback. 508396d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein return getCachedCallingPackage(); 508496d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein } 508596d67800c827efb5c08adaa32e221aaae53d02a4Sam Blitzstein 50867a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final PackageManager pm = getContext().getPackageManager(); 50877a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final int uid = Binder.getCallingUid(); 50887a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String[] packages = pm.getPackagesForUid(uid); 50897a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (packages != null && packages.length == 1) { 50907a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert return packages[0]; 50917a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 50927a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String name = pm.getNameForUid(uid); 50937a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (name != null) { 50947a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert return name; 50957a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 50967a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert return String.valueOf(uid); 50977a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 50987a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert 50997a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert private void addMutator(ContentValues values, String columnName) { 51007a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String packageName = getCallingPackageName(); 51017a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert final String mutators = values.getAsString(columnName); 51027a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert if (TextUtils.isEmpty(mutators)) { 51037a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert values.put(columnName, packageName); 51047a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } else { 51057a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert values.put(columnName, mutators + "," + packageName); 51067a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 51077a2531a4b68e9efe14353cc7f4a64a8c5613e5d3Alon Albert } 51089f005e4843925efe4fa8434361c4ad4ad384ed4cKen Shirriff} 5109