ContactsProvider2Test.java revision ac13ddd04d665442de846b59234bdc936a6699b4
190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/*
290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Copyright (C) 2009 The Android Open Source Project
390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *
490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Licensed under the Apache License, Version 2.0 (the "License");
590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * you may not use this file except in compliance with the License.
690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * You may obtain a copy of the License at
790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *      http://www.apache.org/licenses/LICENSE-2.0
990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *
1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Unless required by applicable law or agreed to in writing, software
1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * distributed under the License is distributed on an "AS IS" BASIS,
1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * See the License for the specific language governing permissions and
1490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * limitations under the License.
1590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */
1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)package com.android.providers.contacts;
1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import com.android.internal.util.ArrayUtils;
200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import com.google.android.collect.Lists;
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.accounts.Account;
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.content.ContentProviderOperation;
260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.ContentProviderResult;
270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.ContentUris;
280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.ContentValues;
290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.Entity;
300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.EntityIterator;
310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.res.AssetFileDescriptor;
320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.database.Cursor;
330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.net.Uri;
340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.provider.ContactsContract;
350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.provider.ContactsContract.AggregationExceptions;
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.Email;
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport android.provider.ContactsContract.CommonDataKinds.Im;
39116680a4aac90f2aa7413d9095a592090648e557Ben Murdochimport android.provider.ContactsContract.CommonDataKinds.Nickname;
40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.Organization;
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.Phone;
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.Photo;
4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.StructuredName;
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import android.provider.ContactsContract.ContactCounts;
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import android.provider.ContactsContract.Contacts;
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import android.provider.ContactsContract.Data;
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import android.provider.ContactsContract.Directory;
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.provider.ContactsContract.DisplayNameSources;
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.provider.ContactsContract.Groups;
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.provider.ContactsContract.PhoneLookup;
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.provider.ContactsContract.PhoneticNameStyle;
533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import android.provider.ContactsContract.ProviderStatus;
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.provider.ContactsContract.RawContacts;
550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.provider.ContactsContract.RawContactsEntity;
563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import android.provider.ContactsContract.SearchSnippetColumns;
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import android.provider.ContactsContract.Settings;
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import android.provider.ContactsContract.StatusUpdates;
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import android.provider.LiveFolders;
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.provider.OpenableColumns;
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.test.MoreAsserts;
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.test.suitebuilder.annotation.LargeTest;
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import java.io.FileInputStream;
653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import java.io.IOException;
663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import java.io.InputStream;
673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import java.text.Collator;
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import java.util.Arrays;
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import java.util.Locale;
70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
71868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/**
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * Unit tests for {@link ContactsProvider2}.
73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) *
7490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Run the test like this:
7590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * <code>
7623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) * adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
7790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * </code>
79868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */
8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)@LargeTest
8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)public class ContactsProvider2Test extends BaseContactsProvider2Test {
8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
8390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    public void testContactsProjection() {
840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        assertProjection(Contacts.CONTENT_URI, new String[]{
850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts._ID,
860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.DISPLAY_NAME_PRIMARY,
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                Contacts.DISPLAY_NAME_ALTERNATIVE,
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                Contacts.DISPLAY_NAME_SOURCE,
890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.PHONETIC_NAME,
900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.PHONETIC_NAME_STYLE,
910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.SORT_KEY_PRIMARY,
920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.SORT_KEY_ALTERNATIVE,
930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.LAST_TIME_CONTACTED,
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Contacts.TIMES_CONTACTED,
9590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.STARRED,
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                Contacts.IN_VISIBLE_GROUP,
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Contacts.PHOTO_ID,
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Contacts.CUSTOM_RINGTONE,
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Contacts.HAS_PHONE_NUMBER,
100e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                Contacts.SEND_TO_VOICEMAIL,
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                Contacts.LOOKUP_KEY,
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                Contacts.NAME_RAW_CONTACT_ID,
10390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.CONTACT_PRESENCE,
10490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.CONTACT_CHAT_CAPABILITY,
10590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.CONTACT_STATUS,
10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.CONTACT_STATUS_TIMESTAMP,
10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.CONTACT_STATUS_RES_PACKAGE,
1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.CONTACT_STATUS_LABEL,
1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                Contacts.CONTACT_STATUS_ICON,
1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        });
1110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    public void testContactsWithSnippetProjection() {
11490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(),
11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            new String[]{
116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                Contacts._ID,
117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                Contacts.DISPLAY_NAME_PRIMARY,
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                Contacts.DISPLAY_NAME_ALTERNATIVE,
11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.DISPLAY_NAME_SOURCE,
12090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.PHONETIC_NAME,
12190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.PHONETIC_NAME_STYLE,
12290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.SORT_KEY_PRIMARY,
12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                Contacts.SORT_KEY_ALTERNATIVE,
124                Contacts.LAST_TIME_CONTACTED,
125                Contacts.TIMES_CONTACTED,
126                Contacts.STARRED,
127                Contacts.IN_VISIBLE_GROUP,
128                Contacts.PHOTO_ID,
129                Contacts.CUSTOM_RINGTONE,
130                Contacts.HAS_PHONE_NUMBER,
131                Contacts.SEND_TO_VOICEMAIL,
132                Contacts.LOOKUP_KEY,
133                Contacts.NAME_RAW_CONTACT_ID,
134                Contacts.CONTACT_PRESENCE,
135                Contacts.CONTACT_CHAT_CAPABILITY,
136                Contacts.CONTACT_STATUS,
137                Contacts.CONTACT_STATUS_TIMESTAMP,
138                Contacts.CONTACT_STATUS_RES_PACKAGE,
139                Contacts.CONTACT_STATUS_LABEL,
140                Contacts.CONTACT_STATUS_ICON,
141
142                SearchSnippetColumns.SNIPPET_MIMETYPE,
143                SearchSnippetColumns.SNIPPET_DATA_ID,
144                SearchSnippetColumns.SNIPPET_DATA1,
145                SearchSnippetColumns.SNIPPET_DATA2,
146                SearchSnippetColumns.SNIPPET_DATA3,
147                SearchSnippetColumns.SNIPPET_DATA4,
148        });
149    }
150
151    public void testRawContactsProjection() {
152        assertProjection(RawContacts.CONTENT_URI, new String[]{
153                RawContacts._ID,
154                RawContacts.CONTACT_ID,
155                RawContacts.ACCOUNT_NAME,
156                RawContacts.ACCOUNT_TYPE,
157                RawContacts.SOURCE_ID,
158                RawContacts.VERSION,
159                RawContacts.DIRTY,
160                RawContacts.DELETED,
161                RawContacts.DISPLAY_NAME_PRIMARY,
162                RawContacts.DISPLAY_NAME_ALTERNATIVE,
163                RawContacts.DISPLAY_NAME_SOURCE,
164                RawContacts.PHONETIC_NAME,
165                RawContacts.PHONETIC_NAME_STYLE,
166                RawContacts.NAME_VERIFIED,
167                RawContacts.SORT_KEY_PRIMARY,
168                RawContacts.SORT_KEY_ALTERNATIVE,
169                RawContacts.TIMES_CONTACTED,
170                RawContacts.LAST_TIME_CONTACTED,
171                RawContacts.CUSTOM_RINGTONE,
172                RawContacts.SEND_TO_VOICEMAIL,
173                RawContacts.STARRED,
174                RawContacts.AGGREGATION_MODE,
175                RawContacts.SYNC1,
176                RawContacts.SYNC2,
177                RawContacts.SYNC3,
178                RawContacts.SYNC4,
179        });
180    }
181
182    public void testDataProjection() {
183        assertProjection(Data.CONTENT_URI, new String[]{
184                Data._ID,
185                Data.RAW_CONTACT_ID,
186                Data.DATA_VERSION,
187                Data.IS_PRIMARY,
188                Data.IS_SUPER_PRIMARY,
189                Data.RES_PACKAGE,
190                Data.MIMETYPE,
191                Data.DATA1,
192                Data.DATA2,
193                Data.DATA3,
194                Data.DATA4,
195                Data.DATA5,
196                Data.DATA6,
197                Data.DATA7,
198                Data.DATA8,
199                Data.DATA9,
200                Data.DATA10,
201                Data.DATA11,
202                Data.DATA12,
203                Data.DATA13,
204                Data.DATA14,
205                Data.DATA15,
206                Data.SYNC1,
207                Data.SYNC2,
208                Data.SYNC3,
209                Data.SYNC4,
210                Data.CONTACT_ID,
211                Data.PRESENCE,
212                Data.CHAT_CAPABILITY,
213                Data.STATUS,
214                Data.STATUS_TIMESTAMP,
215                Data.STATUS_RES_PACKAGE,
216                Data.STATUS_LABEL,
217                Data.STATUS_ICON,
218                RawContacts.ACCOUNT_NAME,
219                RawContacts.ACCOUNT_TYPE,
220                RawContacts.SOURCE_ID,
221                RawContacts.VERSION,
222                RawContacts.DIRTY,
223                RawContacts.NAME_VERIFIED,
224                Contacts._ID,
225                Contacts.DISPLAY_NAME_PRIMARY,
226                Contacts.DISPLAY_NAME_ALTERNATIVE,
227                Contacts.DISPLAY_NAME_SOURCE,
228                Contacts.PHONETIC_NAME,
229                Contacts.PHONETIC_NAME_STYLE,
230                Contacts.SORT_KEY_PRIMARY,
231                Contacts.SORT_KEY_ALTERNATIVE,
232                Contacts.LAST_TIME_CONTACTED,
233                Contacts.TIMES_CONTACTED,
234                Contacts.STARRED,
235                Contacts.IN_VISIBLE_GROUP,
236                Contacts.PHOTO_ID,
237                Contacts.CUSTOM_RINGTONE,
238                Contacts.SEND_TO_VOICEMAIL,
239                Contacts.LOOKUP_KEY,
240                Contacts.NAME_RAW_CONTACT_ID,
241                Contacts.CONTACT_PRESENCE,
242                Contacts.CONTACT_CHAT_CAPABILITY,
243                Contacts.CONTACT_STATUS,
244                Contacts.CONTACT_STATUS_TIMESTAMP,
245                Contacts.CONTACT_STATUS_RES_PACKAGE,
246                Contacts.CONTACT_STATUS_LABEL,
247                Contacts.CONTACT_STATUS_ICON,
248                GroupMembership.GROUP_SOURCE_ID,
249        });
250    }
251
252    public void testDistinctDataProjection() {
253        assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
254            new String[]{
255                Data._ID,
256                Data.DATA_VERSION,
257                Data.IS_PRIMARY,
258                Data.IS_SUPER_PRIMARY,
259                Data.RES_PACKAGE,
260                Data.MIMETYPE,
261                Data.DATA1,
262                Data.DATA2,
263                Data.DATA3,
264                Data.DATA4,
265                Data.DATA5,
266                Data.DATA6,
267                Data.DATA7,
268                Data.DATA8,
269                Data.DATA9,
270                Data.DATA10,
271                Data.DATA11,
272                Data.DATA12,
273                Data.DATA13,
274                Data.DATA14,
275                Data.DATA15,
276                Data.SYNC1,
277                Data.SYNC2,
278                Data.SYNC3,
279                Data.SYNC4,
280                Data.CONTACT_ID,
281                Data.PRESENCE,
282                Data.CHAT_CAPABILITY,
283                Data.STATUS,
284                Data.STATUS_TIMESTAMP,
285                Data.STATUS_RES_PACKAGE,
286                Data.STATUS_LABEL,
287                Data.STATUS_ICON,
288                Contacts._ID,
289                Contacts.DISPLAY_NAME_PRIMARY,
290                Contacts.DISPLAY_NAME_ALTERNATIVE,
291                Contacts.DISPLAY_NAME_SOURCE,
292                Contacts.PHONETIC_NAME,
293                Contacts.PHONETIC_NAME_STYLE,
294                Contacts.SORT_KEY_PRIMARY,
295                Contacts.SORT_KEY_ALTERNATIVE,
296                Contacts.LAST_TIME_CONTACTED,
297                Contacts.TIMES_CONTACTED,
298                Contacts.STARRED,
299                Contacts.IN_VISIBLE_GROUP,
300                Contacts.PHOTO_ID,
301                Contacts.CUSTOM_RINGTONE,
302                Contacts.SEND_TO_VOICEMAIL,
303                Contacts.LOOKUP_KEY,
304                Contacts.CONTACT_PRESENCE,
305                Contacts.CONTACT_CHAT_CAPABILITY,
306                Contacts.CONTACT_STATUS,
307                Contacts.CONTACT_STATUS_TIMESTAMP,
308                Contacts.CONTACT_STATUS_RES_PACKAGE,
309                Contacts.CONTACT_STATUS_LABEL,
310                Contacts.CONTACT_STATUS_ICON,
311                GroupMembership.GROUP_SOURCE_ID,
312        });
313    }
314
315    public void testEntityProjection() {
316        assertProjection(
317            Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0),
318                    Contacts.Entity.CONTENT_DIRECTORY),
319            new String[]{
320                Contacts.Entity._ID,
321                Contacts.Entity.DATA_ID,
322                Contacts.Entity.RAW_CONTACT_ID,
323                Data.DATA_VERSION,
324                Data.IS_PRIMARY,
325                Data.IS_SUPER_PRIMARY,
326                Data.RES_PACKAGE,
327                Data.MIMETYPE,
328                Data.DATA1,
329                Data.DATA2,
330                Data.DATA3,
331                Data.DATA4,
332                Data.DATA5,
333                Data.DATA6,
334                Data.DATA7,
335                Data.DATA8,
336                Data.DATA9,
337                Data.DATA10,
338                Data.DATA11,
339                Data.DATA12,
340                Data.DATA13,
341                Data.DATA14,
342                Data.DATA15,
343                Data.SYNC1,
344                Data.SYNC2,
345                Data.SYNC3,
346                Data.SYNC4,
347                Data.CONTACT_ID,
348                Data.PRESENCE,
349                Data.CHAT_CAPABILITY,
350                Data.STATUS,
351                Data.STATUS_TIMESTAMP,
352                Data.STATUS_RES_PACKAGE,
353                Data.STATUS_LABEL,
354                Data.STATUS_ICON,
355                RawContacts.ACCOUNT_NAME,
356                RawContacts.ACCOUNT_TYPE,
357                RawContacts.SOURCE_ID,
358                RawContacts.VERSION,
359                RawContacts.DELETED,
360                RawContacts.DIRTY,
361                RawContacts.NAME_VERIFIED,
362                RawContacts.SYNC1,
363                RawContacts.SYNC2,
364                RawContacts.SYNC3,
365                RawContacts.SYNC4,
366                RawContacts.IS_RESTRICTED,
367                Contacts._ID,
368                Contacts.DISPLAY_NAME_PRIMARY,
369                Contacts.DISPLAY_NAME_ALTERNATIVE,
370                Contacts.DISPLAY_NAME_SOURCE,
371                Contacts.PHONETIC_NAME,
372                Contacts.PHONETIC_NAME_STYLE,
373                Contacts.SORT_KEY_PRIMARY,
374                Contacts.SORT_KEY_ALTERNATIVE,
375                Contacts.LAST_TIME_CONTACTED,
376                Contacts.TIMES_CONTACTED,
377                Contacts.STARRED,
378                Contacts.IN_VISIBLE_GROUP,
379                Contacts.PHOTO_ID,
380                Contacts.CUSTOM_RINGTONE,
381                Contacts.SEND_TO_VOICEMAIL,
382                Contacts.LOOKUP_KEY,
383                Contacts.NAME_RAW_CONTACT_ID,
384                Contacts.CONTACT_PRESENCE,
385                Contacts.CONTACT_CHAT_CAPABILITY,
386                Contacts.CONTACT_STATUS,
387                Contacts.CONTACT_STATUS_TIMESTAMP,
388                Contacts.CONTACT_STATUS_RES_PACKAGE,
389                Contacts.CONTACT_STATUS_LABEL,
390                Contacts.CONTACT_STATUS_ICON,
391                GroupMembership.GROUP_SOURCE_ID,
392        });
393    }
394
395    public void testRawEntityProjection() {
396        assertProjection(RawContactsEntity.CONTENT_URI, new String[]{
397                RawContacts.Entity.DATA_ID,
398                RawContacts._ID,
399                RawContacts.CONTACT_ID,
400                RawContacts.ACCOUNT_NAME,
401                RawContacts.ACCOUNT_TYPE,
402                RawContacts.SOURCE_ID,
403                RawContacts.VERSION,
404                RawContacts.DIRTY,
405                RawContacts.NAME_VERIFIED,
406                RawContacts.DELETED,
407                RawContacts.IS_RESTRICTED,
408                RawContacts.SYNC1,
409                RawContacts.SYNC2,
410                RawContacts.SYNC3,
411                RawContacts.SYNC4,
412                RawContacts.STARRED,
413                Data.DATA_VERSION,
414                Data.IS_PRIMARY,
415                Data.IS_SUPER_PRIMARY,
416                Data.RES_PACKAGE,
417                Data.MIMETYPE,
418                Data.DATA1,
419                Data.DATA2,
420                Data.DATA3,
421                Data.DATA4,
422                Data.DATA5,
423                Data.DATA6,
424                Data.DATA7,
425                Data.DATA8,
426                Data.DATA9,
427                Data.DATA10,
428                Data.DATA11,
429                Data.DATA12,
430                Data.DATA13,
431                Data.DATA14,
432                Data.DATA15,
433                Data.SYNC1,
434                Data.SYNC2,
435                Data.SYNC3,
436                Data.SYNC4,
437                GroupMembership.GROUP_SOURCE_ID,
438        });
439    }
440
441    public void testPhoneLookupProjection() {
442        assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
443            new String[]{
444                PhoneLookup._ID,
445                PhoneLookup.LOOKUP_KEY,
446                PhoneLookup.DISPLAY_NAME,
447                PhoneLookup.LAST_TIME_CONTACTED,
448                PhoneLookup.TIMES_CONTACTED,
449                PhoneLookup.STARRED,
450                PhoneLookup.IN_VISIBLE_GROUP,
451                PhoneLookup.PHOTO_ID,
452                PhoneLookup.CUSTOM_RINGTONE,
453                PhoneLookup.HAS_PHONE_NUMBER,
454                PhoneLookup.SEND_TO_VOICEMAIL,
455                PhoneLookup.NUMBER,
456                PhoneLookup.TYPE,
457                PhoneLookup.LABEL,
458        });
459    }
460
461    public void testGroupsProjection() {
462        assertProjection(Groups.CONTENT_URI, new String[]{
463                Groups._ID,
464                Groups.ACCOUNT_NAME,
465                Groups.ACCOUNT_TYPE,
466                Groups.SOURCE_ID,
467                Groups.DIRTY,
468                Groups.VERSION,
469                Groups.RES_PACKAGE,
470                Groups.TITLE,
471                Groups.TITLE_RES,
472                Groups.GROUP_VISIBLE,
473                Groups.SYSTEM_ID,
474                Groups.DELETED,
475                Groups.NOTES,
476                Groups.SHOULD_SYNC,
477                Groups.FAVORITES,
478                Groups.AUTO_ADD,
479                Groups.SYNC1,
480                Groups.SYNC2,
481                Groups.SYNC3,
482                Groups.SYNC4,
483        });
484    }
485
486    public void testGroupsSummaryProjection() {
487        assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{
488                Groups._ID,
489                Groups.ACCOUNT_NAME,
490                Groups.ACCOUNT_TYPE,
491                Groups.SOURCE_ID,
492                Groups.DIRTY,
493                Groups.VERSION,
494                Groups.RES_PACKAGE,
495                Groups.TITLE,
496                Groups.TITLE_RES,
497                Groups.GROUP_VISIBLE,
498                Groups.SYSTEM_ID,
499                Groups.DELETED,
500                Groups.NOTES,
501                Groups.SHOULD_SYNC,
502                Groups.FAVORITES,
503                Groups.AUTO_ADD,
504                Groups.SYNC1,
505                Groups.SYNC2,
506                Groups.SYNC3,
507                Groups.SYNC4,
508                Groups.SUMMARY_COUNT,
509                Groups.SUMMARY_WITH_PHONES,
510        });
511    }
512
513    public void testAggregateExceptionProjection() {
514        assertProjection(AggregationExceptions.CONTENT_URI, new String[]{
515                AggregationExceptionColumns._ID,
516                AggregationExceptions.TYPE,
517                AggregationExceptions.RAW_CONTACT_ID1,
518                AggregationExceptions.RAW_CONTACT_ID2,
519        });
520    }
521
522    public void testSettingsProjection() {
523        assertProjection(Settings.CONTENT_URI, new String[]{
524                Settings.ACCOUNT_NAME,
525                Settings.ACCOUNT_TYPE,
526                Settings.UNGROUPED_VISIBLE,
527                Settings.SHOULD_SYNC,
528                Settings.ANY_UNSYNCED,
529                Settings.UNGROUPED_COUNT,
530                Settings.UNGROUPED_WITH_PHONES,
531        });
532    }
533
534    public void testStatusUpdatesProjection() {
535        assertProjection(StatusUpdates.CONTENT_URI, new String[]{
536                PresenceColumns.RAW_CONTACT_ID,
537                StatusUpdates.DATA_ID,
538                StatusUpdates.IM_ACCOUNT,
539                StatusUpdates.IM_HANDLE,
540                StatusUpdates.PROTOCOL,
541                StatusUpdates.CUSTOM_PROTOCOL,
542                StatusUpdates.PRESENCE,
543                StatusUpdates.CHAT_CAPABILITY,
544                StatusUpdates.STATUS,
545                StatusUpdates.STATUS_TIMESTAMP,
546                StatusUpdates.STATUS_RES_PACKAGE,
547                StatusUpdates.STATUS_ICON,
548                StatusUpdates.STATUS_LABEL,
549        });
550    }
551
552    public void testLiveFoldersProjection() {
553        assertProjection(
554            Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "live_folders/contacts"),
555            new String[]{
556                LiveFolders._ID,
557                LiveFolders.NAME,
558        });
559    }
560
561    public void testDirectoryProjection() {
562        assertProjection(Directory.CONTENT_URI, new String[]{
563                Directory._ID,
564                Directory.PACKAGE_NAME,
565                Directory.TYPE_RESOURCE_ID,
566                Directory.DISPLAY_NAME,
567                Directory.DIRECTORY_AUTHORITY,
568                Directory.ACCOUNT_TYPE,
569                Directory.ACCOUNT_NAME,
570                Directory.EXPORT_SUPPORT,
571        });
572    }
573
574    public void testRawContactsInsert() {
575        ContentValues values = new ContentValues();
576
577        values.put(RawContacts.ACCOUNT_NAME, "a");
578        values.put(RawContacts.ACCOUNT_TYPE, "b");
579        values.put(RawContacts.SOURCE_ID, "c");
580        values.put(RawContacts.VERSION, 42);
581        values.put(RawContacts.DIRTY, 1);
582        values.put(RawContacts.DELETED, 1);
583        values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
584        values.put(RawContacts.CUSTOM_RINGTONE, "d");
585        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
586        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
587        values.put(RawContacts.STARRED, 1);
588        values.put(RawContacts.SYNC1, "e");
589        values.put(RawContacts.SYNC2, "f");
590        values.put(RawContacts.SYNC3, "g");
591        values.put(RawContacts.SYNC4, "h");
592
593        Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
594        long rawContactId = ContentUris.parseId(rowUri);
595
596        assertStoredValues(rowUri, values);
597        assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId);
598        assertNetworkNotified(true);
599    }
600
601    public void testDataDirectoryWithLookupUri() {
602        ContentValues values = new ContentValues();
603
604        long rawContactId = createRawContactWithName();
605        insertPhoneNumber(rawContactId, "555-GOOG-411");
606        insertEmail(rawContactId, "google@android.com");
607
608        long contactId = queryContactId(rawContactId);
609        String lookupKey = queryLookupKey(contactId);
610
611        // Complete and valid lookup URI
612        Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
613        Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
614
615        assertDataRows(dataUri, values);
616
617        // Complete but stale lookup URI
618        lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
619        dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
620        assertDataRows(dataUri, values);
621
622        // Incomplete lookup URI (lookup key only, no contact ID)
623        dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
624                lookupKey), Contacts.Data.CONTENT_DIRECTORY);
625        assertDataRows(dataUri, values);
626    }
627
628    private void assertDataRows(Uri dataUri, ContentValues values) {
629        Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
630        assertEquals(3, cursor.getCount());
631        cursor.moveToFirst();
632        values.put(Data.DATA1, "John Doe");
633        assertCursorValues(cursor, values);
634
635        cursor.moveToNext();
636        values.put(Data.DATA1, "555-GOOG-411");
637        assertCursorValues(cursor, values);
638
639        cursor.moveToNext();
640        values.put(Data.DATA1, "google@android.com");
641        assertCursorValues(cursor, values);
642
643        cursor.close();
644    }
645
646    public void testContactEntitiesWithIdBasedUri() {
647        ContentValues values = new ContentValues();
648        Account account1 = new Account("act1", "actype1");
649        Account account2 = new Account("act2", "actype2");
650
651        long rawContactId1 = createRawContactWithName(account1);
652        insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
653        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
654                StatusUpdates.CAPABILITY_HAS_CAMERA);
655
656        long rawContactId2 = createRawContact(account2);
657        setAggregationException(
658                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
659
660        long contactId = queryContactId(rawContactId1);
661
662        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
663        Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
664
665        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
666    }
667
668    public void testContactEntitiesWithLookupUri() {
669        ContentValues values = new ContentValues();
670        Account account1 = new Account("act1", "actype1");
671        Account account2 = new Account("act2", "actype2");
672
673        long rawContactId1 = createRawContactWithName(account1);
674        insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
675        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
676                StatusUpdates.CAPABILITY_HAS_CAMERA);
677
678        long rawContactId2 = createRawContact(account2);
679        setAggregationException(
680                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
681
682        long contactId = queryContactId(rawContactId1);
683        String lookupKey = queryLookupKey(contactId);
684
685        // First try with a matching contact ID
686        Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
687        Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
688        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
689
690        // Now try with a contact ID mismatch
691        contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
692        entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
693        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
694
695        // Now try without an ID altogether
696        contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
697        entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
698        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
699    }
700
701    private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1,
702            long rawContactId2) {
703        ContentValues values = new ContentValues();
704
705        Cursor cursor = mResolver.query(entityUri, null, null, null,
706                Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID);
707        assertEquals(3, cursor.getCount());
708
709        // First row - name
710        cursor.moveToFirst();
711        values.put(Contacts.Entity.CONTACT_ID, contactId);
712        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
713        values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
714        values.put(Contacts.Entity.DATA1, "John Doe");
715        values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
716        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
717        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
718        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
719        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
720        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
721        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
722        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
723        values.putNull(Contacts.Entity.PRESENCE);
724        assertCursorValues(cursor, values);
725
726        // Second row - IM
727        cursor.moveToNext();
728        values.put(Contacts.Entity.CONTACT_ID, contactId);
729        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
730        values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE);
731        values.put(Contacts.Entity.DATA1, "gtalk");
732        values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
733        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
734        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
735        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
736        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
737        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
738        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
739        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
740        values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE);
741        assertCursorValues(cursor, values);
742
743        // Third row - second raw contact, not data
744        cursor.moveToNext();
745        values.put(Contacts.Entity.CONTACT_ID, contactId);
746        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2);
747        values.putNull(Contacts.Entity.MIMETYPE);
748        values.putNull(Contacts.Entity.DATA_ID);
749        values.putNull(Contacts.Entity.DATA1);
750        values.put(Contacts.Entity.ACCOUNT_NAME, "act2");
751        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2");
752        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
753        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
754        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
755        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
756        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
757        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
758        values.putNull(Contacts.Entity.PRESENCE);
759        assertCursorValues(cursor, values);
760
761        cursor.close();
762    }
763
764    public void testDataInsert() {
765        long rawContactId = createRawContactWithName("John", "Doe");
766
767        ContentValues values = new ContentValues();
768        putDataValues(values, rawContactId);
769        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
770        long dataId = ContentUris.parseId(dataUri);
771
772        long contactId = queryContactId(rawContactId);
773        values.put(RawContacts.CONTACT_ID, contactId);
774        assertStoredValues(dataUri, values);
775
776        assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
777
778        // Access the same data through the directory under RawContacts
779        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
780        Uri rawContactDataUri =
781                Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
782        assertSelection(rawContactDataUri, values, Data._ID, dataId);
783
784        // Access the same data through the directory under Contacts
785        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
786        Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
787        assertSelection(contactDataUri, values, Data._ID, dataId);
788        assertNetworkNotified(true);
789    }
790
791    public void testRawContactDataQuery() {
792        Account account1 = new Account("a", "b");
793        Account account2 = new Account("c", "d");
794        long rawContactId1 = createRawContact(account1);
795        Uri dataUri1 = insertStructuredName(rawContactId1, "John", "Doe");
796        long rawContactId2 = createRawContact(account2);
797        Uri dataUri2 = insertStructuredName(rawContactId2, "Jane", "Doe");
798
799        Uri uri1 = maybeAddAccountQueryParameters(dataUri1, account1);
800        Uri uri2 = maybeAddAccountQueryParameters(dataUri2, account2);
801        assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
802        assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
803    }
804
805    public void testPhonesQuery() {
806
807        ContentValues values = new ContentValues();
808        values.put(RawContacts.CUSTOM_RINGTONE, "d");
809        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
810        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
811        values.put(RawContacts.TIMES_CONTACTED, 54321);
812        values.put(RawContacts.STARRED, 1);
813
814        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
815        long rawContactId = ContentUris.parseId(rawContactUri);
816
817        insertStructuredName(rawContactId, "Meghan", "Knox");
818        Uri uri = insertPhoneNumber(rawContactId, "18004664411");
819        long phoneId = ContentUris.parseId(uri);
820
821
822        long contactId = queryContactId(rawContactId);
823        values.clear();
824        values.put(Data._ID, phoneId);
825        values.put(Data.RAW_CONTACT_ID, rawContactId);
826        values.put(RawContacts.CONTACT_ID, contactId);
827        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
828        values.put(Phone.NUMBER, "18004664411");
829        values.put(Phone.TYPE, Phone.TYPE_HOME);
830        values.putNull(Phone.LABEL);
831        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
832        values.put(Contacts.CUSTOM_RINGTONE, "d");
833        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
834        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
835        values.put(Contacts.TIMES_CONTACTED, 54321);
836        values.put(Contacts.STARRED, 1);
837
838        assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
839        assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId);
840    }
841
842    public void testPhonesFilterQuery() {
843        long rawContactId1 = createRawContactWithName("Hot", "Tamale");
844        insertPhoneNumber(rawContactId1, "18004664411");
845        insertPhoneNumber(rawContactId1, "1-800-466-4411");
846
847        long rawContactId2 = createRawContactWithName("Hot", "Tamale");
848        insertPhoneNumber(rawContactId2, "1-800-466-4411");
849
850        Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale");
851        ContentValues values = new ContentValues();
852        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
853        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
854        values.put(Phone.NUMBER, "1-800-466-4411");
855        values.put(Phone.TYPE, Phone.TYPE_HOME);
856        values.putNull(Phone.LABEL);
857        assertStoredValuesWithProjection(filterUri1, values);
858
859        Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411");
860        assertStoredValues(filterUri2, values);
861
862        Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664");
863        assertStoredValues(filterUri3, values);
864
865        Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada");
866        assertEquals(0, getCount(filterUri4, null, null));
867
868        Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*");
869        assertEquals(0, getCount(filterUri5, null, null));
870    }
871
872    public void testPhoneLookup() {
873        ContentValues values = new ContentValues();
874        values.put(RawContacts.CUSTOM_RINGTONE, "d");
875        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
876
877        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
878        long rawContactId = ContentUris.parseId(rawContactUri);
879
880        insertStructuredName(rawContactId, "Hot", "Tamale");
881        insertPhoneNumber(rawContactId, "18004664411");
882
883        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
884
885        values.clear();
886        values.put(PhoneLookup._ID, queryContactId(rawContactId));
887        values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
888        values.put(PhoneLookup.NUMBER, "18004664411");
889        values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
890        values.putNull(PhoneLookup.LABEL);
891        values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
892        values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
893        assertStoredValues(lookupUri1, values);
894
895        // In the context that 8004664411 is a valid number, "4664411" as a
896        // call id should not match to "8004664411"
897        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
898        assertEquals(0, getCount(lookupUri2, null, null));
899    }
900
901    public void testPhoneLookupUseCases() {
902        ContentValues values = new ContentValues();
903        Uri rawContactUri;
904        long rawContactId;
905        Uri lookupUri2;
906
907        values.put(RawContacts.CUSTOM_RINGTONE, "d");
908        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
909
910        // International format in contacts
911        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
912        rawContactId = ContentUris.parseId(rawContactUri);
913
914        insertStructuredName(rawContactId, "Hot", "Tamale");
915        insertPhoneNumber(rawContactId, "+1-650-861-0000");
916
917        values.clear();
918
919        // match with international format
920        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000");
921        assertEquals(1, getCount(lookupUri2, null, null));
922
923        // match with national format
924        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
925        assertEquals(1, getCount(lookupUri2, null, null));
926
927        // National format in contacts
928        values.clear();
929        values.put(RawContacts.CUSTOM_RINGTONE, "d");
930        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
931        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
932        rawContactId = ContentUris.parseId(rawContactUri);
933
934        insertStructuredName(rawContactId, "Hot1", "Tamale");
935        insertPhoneNumber(rawContactId, "650-861-0001");
936
937        values.clear();
938
939        // match with international format
940        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001");
941        assertEquals(2, getCount(lookupUri2, null, null));
942
943        // match with national format
944        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001");
945        assertEquals(2, getCount(lookupUri2, null, null));
946
947        // Local format in contacts
948        values.clear();
949        values.put(RawContacts.CUSTOM_RINGTONE, "d");
950        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
951        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
952        rawContactId = ContentUris.parseId(rawContactUri);
953
954        insertStructuredName(rawContactId, "Hot2", "Tamale");
955        insertPhoneNumber(rawContactId, "861-0002");
956
957        values.clear();
958
959        // match with international format
960        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002");
961        assertEquals(1, getCount(lookupUri2, null, null));
962
963        // match with national format
964        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002");
965        assertEquals(1, getCount(lookupUri2, null, null));
966    }
967
968    public void testPhoneUpdate() {
969        ContentValues values = new ContentValues();
970        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
971        long rawContactId = ContentUris.parseId(rawContactUri);
972
973        insertStructuredName(rawContactId, "Hot", "Tamale");
974        Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
975
976        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
977        assertStoredValue(lookupUri1, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
978
979        values.clear();
980        values.put(Phone.NUMBER, "18004664422");
981        mResolver.update(phoneUri, values, null, null);
982
983        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
984        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
985
986        // Setting number to null will remove the phone lookup record
987        values.clear();
988        values.putNull(Phone.NUMBER);
989        mResolver.update(phoneUri, values, null, null);
990
991        assertEquals(0, getCount(lookupUri2, null, null));
992
993        // Let's restore that phone lookup record
994        values.clear();
995        values.put(Phone.NUMBER, "18004664422");
996        mResolver.update(phoneUri, values, null, null);
997        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
998        assertNetworkNotified(true);
999    }
1000
1001    public void testEmailsQuery() {
1002        ContentValues values = new ContentValues();
1003        values.put(RawContacts.CUSTOM_RINGTONE, "d");
1004        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
1005        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
1006        values.put(RawContacts.TIMES_CONTACTED, 54321);
1007        values.put(RawContacts.STARRED, 1);
1008
1009        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1010        long rawContactId = ContentUris.parseId(rawContactUri);
1011
1012        insertStructuredName(rawContactId, "Meghan", "Knox");
1013        Uri uri = insertEmail(rawContactId, "meghan@acme.com");
1014        long emailId = ContentUris.parseId(uri);
1015
1016        long contactId = queryContactId(rawContactId);
1017        values.clear();
1018        values.put(Data._ID, emailId);
1019        values.put(Data.RAW_CONTACT_ID, rawContactId);
1020        values.put(RawContacts.CONTACT_ID, contactId);
1021        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1022        values.put(Email.DATA, "meghan@acme.com");
1023        values.put(Email.TYPE, Email.TYPE_HOME);
1024        values.putNull(Email.LABEL);
1025        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
1026        values.put(Contacts.CUSTOM_RINGTONE, "d");
1027        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
1028        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
1029        values.put(Contacts.TIMES_CONTACTED, 54321);
1030        values.put(Contacts.STARRED, 1);
1031
1032        assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
1033        assertSelection(Email.CONTENT_URI, values, Data._ID, emailId);
1034    }
1035
1036    public void testEmailsLookupQuery() {
1037        long rawContactId = createRawContactWithName("Hot", "Tamale");
1038        insertEmail(rawContactId, "tamale@acme.com");
1039
1040        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale@acme.com");
1041        ContentValues values = new ContentValues();
1042        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
1043        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1044        values.put(Email.DATA, "tamale@acme.com");
1045        values.put(Email.TYPE, Email.TYPE_HOME);
1046        values.putNull(Email.LABEL);
1047        assertStoredValues(filterUri1, values);
1048
1049        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale@acme.com>");
1050        assertStoredValues(filterUri2, values);
1051
1052        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada@acme.com");
1053        assertEquals(0, getCount(filterUri3, null, null));
1054    }
1055
1056    public void testEmailsFilterQuery() {
1057        long rawContactId1 = createRawContactWithName("Hot", "Tamale");
1058        insertEmail(rawContactId1, "tamale@acme.com");
1059        insertEmail(rawContactId1, "tamale@acme.com");
1060
1061        long rawContactId2 = createRawContactWithName("Hot", "Tamale");
1062        insertEmail(rawContactId2, "tamale@acme.com");
1063
1064        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
1065        ContentValues values = new ContentValues();
1066        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
1067        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1068        values.put(Email.DATA, "tamale@acme.com");
1069        values.put(Email.TYPE, Email.TYPE_HOME);
1070        values.putNull(Email.LABEL);
1071        assertStoredValuesWithProjection(filterUri1, values);
1072
1073        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
1074        assertStoredValuesWithProjection(filterUri2, values);
1075
1076        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hottamale");
1077        assertStoredValuesWithProjection(filterUri3, values);
1078
1079        Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
1080        assertStoredValuesWithProjection(filterUri4, values);
1081
1082        Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
1083        assertEquals(0, getCount(filterUri5, null, null));
1084    }
1085
1086    public void testPostalsQuery() {
1087        long rawContactId = createRawContactWithName("Alice", "Nextore");
1088        Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
1089        long dataId = ContentUris.parseId(dataUri);
1090
1091        long contactId = queryContactId(rawContactId);
1092        ContentValues values = new ContentValues();
1093        values.put(Data._ID, dataId);
1094        values.put(Data.RAW_CONTACT_ID, rawContactId);
1095        values.put(RawContacts.CONTACT_ID, contactId);
1096        values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
1097        values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
1098        values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
1099
1100        assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
1101                values);
1102        assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
1103    }
1104
1105    public void testQueryContactData() {
1106        ContentValues values = new ContentValues();
1107        long contactId = createContact(values, "John", "Doe",
1108                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
1109                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
1110        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1111
1112        assertStoredValues(contactUri, values);
1113        assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
1114    }
1115
1116    public void testQueryContactWithStatusUpdate() {
1117        ContentValues values = new ContentValues();
1118        long contactId = createContact(values, "John", "Doe",
1119                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
1120                StatusUpdates.CAPABILITY_HAS_CAMERA);
1121        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1122        values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1123        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1124        assertStoredValuesWithProjection(contactUri, values);
1125        assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
1126    }
1127
1128    public void testQueryContactFilter() {
1129        ContentValues values = new ContentValues();
1130        long rawContactId = createRawContact(values, "18004664411",
1131                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
1132                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
1133                StatusUpdates.CAPABILITY_HAS_VOICE);
1134
1135        ContentValues nameValues = new ContentValues();
1136        nameValues.put(StructuredName.GIVEN_NAME, "Stu");
1137        nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
1138        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
1139        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
1140        Uri nameUri = insertStructuredName(rawContactId, nameValues);
1141
1142        long contactId = queryContactId(rawContactId);
1143        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1144
1145        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
1146        assertStoredValuesWithProjection(filterUri1, values);
1147
1148        assertContactFilter(contactId, "goolash");
1149        assertContactFilter(contactId, "lash");
1150
1151        Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goolish");
1152        assertEquals(0, getCount(filterUri2, null, null));
1153
1154        // Phonetic name with given/family reversed should not match
1155        Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "lashgoo");
1156        assertEquals(0, getCount(filterUri3, null, null));
1157
1158        nameValues.clear();
1159        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
1160        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
1161
1162        mResolver.update(nameUri, nameValues, null, null);
1163
1164        assertContactFilter(contactId, "galosh");
1165
1166        Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goolish");
1167        assertEquals(0, getCount(filterUri4, null, null));
1168    }
1169
1170    public void testQueryContactStrequent() {
1171        ContentValues values1 = new ContentValues();
1172        createContact(values1, "Noah", "Tever", "18004664411",
1173                "a@acme.com", StatusUpdates.OFFLINE, 0, 0, 0,
1174                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
1175        ContentValues values2 = new ContentValues();
1176        createContact(values2, "Sam", "Times", "18004664412",
1177                "b@acme.com", StatusUpdates.INVISIBLE, 3, 0, 0,
1178                StatusUpdates.CAPABILITY_HAS_CAMERA);
1179        ContentValues values3 = new ContentValues();
1180        createContact(values3, "Lotta", "Calling", "18004664413",
1181                "c@acme.com", StatusUpdates.AWAY, 5, 0, 0,
1182                StatusUpdates.CAPABILITY_HAS_VIDEO);
1183        ContentValues values4 = new ContentValues();
1184        createContact(values4, "Fay", "Veritt", "18004664414",
1185                "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0,
1186                StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE);
1187
1188        Cursor c = mResolver.query(Contacts.CONTENT_STREQUENT_URI, null, null, null,
1189                Contacts._ID);
1190        assertEquals(3, c.getCount());
1191        c.moveToFirst();
1192        assertCursorValues(c, values4);
1193        c.moveToNext();
1194        assertCursorValues(c, values3);
1195        c.moveToNext();
1196        assertCursorValues(c, values2);
1197        c.close();
1198
1199        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay");
1200        c = mResolver.query(filterUri, null, null, null, Contacts._ID);
1201        assertEquals(1, c.getCount());
1202        c.moveToFirst();
1203        assertCursorValues(c, values4);
1204        c.close();
1205    }
1206
1207    public void testQueryContactGroup() {
1208        long groupId = createGroup(null, "testGroup", "Test Group");
1209
1210        ContentValues values1 = new ContentValues();
1211        createContact(values1, "Best", "West", "18004664411",
1212                "west@acme.com", StatusUpdates.OFFLINE, 0, 0, groupId,
1213                StatusUpdates.CAPABILITY_HAS_CAMERA);
1214
1215        ContentValues values2 = new ContentValues();
1216        createContact(values2, "Rest", "East", "18004664422",
1217                "east@acme.com", StatusUpdates.AVAILABLE, 0, 0, 0,
1218                StatusUpdates.CAPABILITY_HAS_VOICE);
1219
1220        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
1221        Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
1222        assertEquals(1, c.getCount());
1223        c.moveToFirst();
1224        assertCursorValues(c, values1);
1225        c.close();
1226
1227        Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
1228        c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
1229                new String[] { "Best West" }, Contacts._ID);
1230        assertEquals(1, c.getCount());
1231        c.close();
1232
1233        Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
1234        c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
1235        assertEquals(0, c.getCount());
1236        c.close();
1237    }
1238
1239    public void testPhonesWithStatusUpdate() {
1240
1241        ContentValues values = new ContentValues();
1242        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1243        long rawContactId = ContentUris.parseId(rawContactUri);
1244        insertStructuredName(rawContactId, "John", "Doe");
1245        Uri photoUri = insertPhoto(rawContactId);
1246        long photoId = ContentUris.parseId(photoUri);
1247        values.put(Contacts.PHOTO_ID, photoId);
1248        insertPhoneNumber(rawContactId, "18004664411");
1249        insertPhoneNumber(rawContactId, "18004664412");
1250        insertEmail(rawContactId, "goog411@acme.com");
1251        insertEmail(rawContactId, "goog412@acme.com");
1252
1253        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
1254                StatusUpdates.INVISIBLE, "Bad",
1255                StatusUpdates.CAPABILITY_HAS_CAMERA);
1256        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412@acme.com",
1257                StatusUpdates.AVAILABLE, "Good",
1258                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE);
1259        long contactId = queryContactId(rawContactId);
1260
1261        Uri uri = Data.CONTENT_URI;
1262
1263        Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND "
1264                + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER);
1265        assertEquals(2, c.getCount());
1266
1267        c.moveToFirst();
1268
1269        values.clear();
1270        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
1271        values.put(Contacts.CONTACT_STATUS, "Bad");
1272        values.put(Contacts.DISPLAY_NAME, "John Doe");
1273        values.put(Phone.NUMBER, "18004664411");
1274        values.putNull(Phone.LABEL);
1275        values.put(RawContacts.CONTACT_ID, contactId);
1276        assertCursorValues(c, values);
1277
1278        c.moveToNext();
1279
1280        values.clear();
1281        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
1282        values.put(Contacts.CONTACT_STATUS, "Bad");
1283        values.put(Contacts.DISPLAY_NAME, "John Doe");
1284        values.put(Phone.NUMBER, "18004664412");
1285        values.putNull(Phone.LABEL);
1286        values.put(RawContacts.CONTACT_ID, contactId);
1287        assertCursorValues(c, values);
1288
1289        c.close();
1290    }
1291
1292    public void testGroupQuery() {
1293        Account account1 = new Account("a", "b");
1294        Account account2 = new Account("c", "d");
1295        long groupId1 = createGroup(account1, "e", "f");
1296        long groupId2 = createGroup(account2, "g", "h");
1297        Uri uri1 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1);
1298        Uri uri2 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2);
1299        assertEquals(1, getCount(uri1, null, null));
1300        assertEquals(1, getCount(uri2, null, null));
1301        assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ;
1302        assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ;
1303    }
1304
1305    public void testGroupInsert() {
1306        ContentValues values = new ContentValues();
1307
1308        values.put(Groups.ACCOUNT_NAME, "a");
1309        values.put(Groups.ACCOUNT_TYPE, "b");
1310        values.put(Groups.SOURCE_ID, "c");
1311        values.put(Groups.VERSION, 42);
1312        values.put(Groups.GROUP_VISIBLE, 1);
1313        values.put(Groups.TITLE, "d");
1314        values.put(Groups.TITLE_RES, 1234);
1315        values.put(Groups.NOTES, "e");
1316        values.put(Groups.RES_PACKAGE, "f");
1317        values.put(Groups.SYSTEM_ID, "g");
1318        values.put(Groups.DELETED, 1);
1319        values.put(Groups.SYNC1, "h");
1320        values.put(Groups.SYNC2, "i");
1321        values.put(Groups.SYNC3, "j");
1322        values.put(Groups.SYNC4, "k");
1323
1324        Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values);
1325
1326        values.put(Groups.DIRTY, 1);
1327        assertStoredValues(rowUri, values);
1328    }
1329
1330    public void testSettingsQuery() {
1331        Account account1 = new Account("a", "b");
1332        Account account2 = new Account("c", "d");
1333        createSettings(account1, "0", "0");
1334        createSettings(account2, "1", "1");
1335        Uri uri1 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1);
1336        Uri uri2 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2);
1337        assertEquals(1, getCount(uri1, null, null));
1338        assertEquals(1, getCount(uri2, null, null));
1339        assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ;
1340        assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0") ;
1341        assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ;
1342        assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1") ;
1343    }
1344
1345    public void testDisplayNameParsingWhenPartsUnspecified() {
1346        long rawContactId = createRawContact();
1347        ContentValues values = new ContentValues();
1348        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
1349        insertStructuredName(rawContactId, values);
1350
1351        assertStructuredName(rawContactId, "Mr", "John", "Kevin", "von Smith", "Jr.");
1352    }
1353
1354    public void testDisplayNameParsingWhenPartsAreNull() {
1355        long rawContactId = createRawContact();
1356        ContentValues values = new ContentValues();
1357        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
1358        values.putNull(StructuredName.GIVEN_NAME);
1359        values.putNull(StructuredName.FAMILY_NAME);
1360        insertStructuredName(rawContactId, values);
1361        assertStructuredName(rawContactId, "Mr", "John", "Kevin", "von Smith", "Jr.");
1362    }
1363
1364    public void testDisplayNameParsingWhenPartsSpecified() {
1365        long rawContactId = createRawContact();
1366        ContentValues values = new ContentValues();
1367        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
1368        values.put(StructuredName.FAMILY_NAME, "Johnson");
1369        insertStructuredName(rawContactId, values);
1370
1371        assertStructuredName(rawContactId, null, null, null, "Johnson", null);
1372    }
1373
1374    public void testContactWithoutPhoneticName() {
1375        final long rawContactId = createRawContact(null);
1376
1377        ContentValues values = new ContentValues();
1378        values.put(StructuredName.PREFIX, "Mr");
1379        values.put(StructuredName.GIVEN_NAME, "John");
1380        values.put(StructuredName.MIDDLE_NAME, "K.");
1381        values.put(StructuredName.FAMILY_NAME, "Doe");
1382        values.put(StructuredName.SUFFIX, "Jr.");
1383        Uri dataUri = insertStructuredName(rawContactId, values);
1384
1385        values.clear();
1386        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1387        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "John K. Doe, Jr.");
1388        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Doe, John K., Jr.");
1389        values.putNull(RawContacts.PHONETIC_NAME);
1390        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1391        values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
1392        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
1393
1394        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1395        assertStoredValues(rawContactUri, values);
1396
1397        values.clear();
1398        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1399        values.put(Contacts.DISPLAY_NAME_PRIMARY, "John K. Doe, Jr.");
1400        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Doe, John K., Jr.");
1401        values.putNull(Contacts.PHONETIC_NAME);
1402        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1403        values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
1404        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
1405
1406        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
1407                queryContactId(rawContactId));
1408        assertStoredValues(contactUri, values);
1409
1410        // The same values should be available through a join with Data
1411        assertStoredValues(dataUri, values);
1412    }
1413
1414    public void testContactWithChineseName() {
1415
1416        // Only run this test when Chinese collation is supported
1417        if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
1418            return;
1419        }
1420
1421        long rawContactId = createRawContact(null);
1422
1423        ContentValues values = new ContentValues();
1424        values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B");
1425        Uri dataUri = insertStructuredName(rawContactId, values);
1426
1427        values.clear();
1428        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1429        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
1430        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
1431        values.putNull(RawContacts.PHONETIC_NAME);
1432        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1433        values.put(RawContacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
1434        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
1435
1436        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1437        assertStoredValues(rawContactUri, values);
1438
1439        values.clear();
1440        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1441        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
1442        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
1443        values.putNull(Contacts.PHONETIC_NAME);
1444        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1445        values.put(Contacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
1446        values.put(Contacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
1447
1448        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
1449                queryContactId(rawContactId));
1450        assertStoredValues(contactUri, values);
1451
1452        // The same values should be available through a join with Data
1453        assertStoredValues(dataUri, values);
1454    }
1455
1456    public void testContactWithJapaneseName() {
1457        long rawContactId = createRawContact(null);
1458
1459        ContentValues values = new ContentValues();
1460        values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
1461        values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
1462        Uri dataUri = insertStructuredName(rawContactId, values);
1463
1464        values.clear();
1465        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1466        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
1467        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
1468        values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
1469        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
1470        values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
1471        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
1472
1473        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1474        assertStoredValues(rawContactUri, values);
1475
1476        values.clear();
1477        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
1478        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
1479        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
1480        values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
1481        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
1482        values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
1483        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
1484
1485        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
1486                queryContactId(rawContactId));
1487        assertStoredValues(contactUri, values);
1488
1489        // The same values should be available through a join with Data
1490        assertStoredValues(dataUri, values);
1491    }
1492
1493    public void testDisplayNameUpdate() {
1494        long rawContactId1 = createRawContact();
1495        insertEmail(rawContactId1, "potato@acme.com", true);
1496
1497        long rawContactId2 = createRawContact();
1498        insertPhoneNumber(rawContactId2, "123456789", true);
1499
1500        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1501                rawContactId1, rawContactId2);
1502
1503        assertAggregated(rawContactId1, rawContactId2, "123456789");
1504
1505        insertStructuredName(rawContactId2, "Potato", "Head");
1506
1507        assertAggregated(rawContactId1, rawContactId2, "Potato Head");
1508        assertNetworkNotified(true);
1509    }
1510
1511    public void testDisplayNameFromData() {
1512        long rawContactId = createRawContact();
1513        long contactId = queryContactId(rawContactId);
1514        ContentValues values = new ContentValues();
1515
1516        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1517
1518        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
1519        insertEmail(rawContactId, "mike@monstersinc.com");
1520        assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike@monstersinc.com");
1521
1522        insertEmail(rawContactId, "james@monstersinc.com", true);
1523        assertStoredValue(uri, Contacts.DISPLAY_NAME, "james@monstersinc.com");
1524
1525        insertPhoneNumber(rawContactId, "1-800-466-4411");
1526        assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411");
1527
1528        // If there are title and company, the company is display name.
1529        values.clear();
1530        values.put(Organization.COMPANY, "Monsters Inc");
1531        Uri organizationUri = insertOrganization(rawContactId, values);
1532        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc");
1533
1534        // If there is nickname, that is display name.
1535        insertNickname(rawContactId, "Sully");
1536        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully");
1537
1538        // If there is structured name, that is display name.
1539        values.clear();
1540        values.put(StructuredName.GIVEN_NAME, "James");
1541        values.put(StructuredName.MIDDLE_NAME, "P.");
1542        values.put(StructuredName.FAMILY_NAME, "Sullivan");
1543        insertStructuredName(rawContactId, values);
1544        assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan");
1545    }
1546
1547    public void testDisplayNameFromOrganizationWithoutPhoneticName() {
1548        long rawContactId = createRawContact();
1549        long contactId = queryContactId(rawContactId);
1550        ContentValues values = new ContentValues();
1551
1552        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1553
1554        // If there is title without company, the title is display name.
1555        values.clear();
1556        values.put(Organization.TITLE, "Protagonist");
1557        Uri organizationUri = insertOrganization(rawContactId, values);
1558        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist");
1559
1560        // If there are title and company, the company is display name.
1561        values.clear();
1562        values.put(Organization.COMPANY, "Monsters Inc");
1563        mResolver.update(organizationUri, values, null, null);
1564
1565        values.clear();
1566        values.put(Contacts.DISPLAY_NAME, "Monsters Inc");
1567        values.putNull(Contacts.PHONETIC_NAME);
1568        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1569        values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc");
1570        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc");
1571        assertStoredValues(uri, values);
1572    }
1573
1574    public void testDisplayNameFromOrganizationWithJapanesePhoneticName() {
1575        long rawContactId = createRawContact();
1576        long contactId = queryContactId(rawContactId);
1577        ContentValues values = new ContentValues();
1578
1579        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1580
1581        // If there is title without company, the title is display name.
1582        values.clear();
1583        values.put(Organization.COMPANY, "DoCoMo");
1584        values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
1585        Uri organizationUri = insertOrganization(rawContactId, values);
1586
1587        values.clear();
1588        values.put(Contacts.DISPLAY_NAME, "DoCoMo");
1589        values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
1590        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
1591        values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2");
1592        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2");
1593        assertStoredValues(uri, values);
1594    }
1595
1596    public void testDisplayNameFromOrganizationWithChineseName() {
1597        boolean hasChineseCollator = false;
1598        final Locale locale[] = Collator.getAvailableLocales();
1599        for (int i = 0; i < locale.length; i++) {
1600            if (locale[i].equals(Locale.CHINA)) {
1601                hasChineseCollator = true;
1602                break;
1603            }
1604        }
1605
1606        if (!hasChineseCollator) {
1607            return;
1608        }
1609
1610        long rawContactId = createRawContact();
1611        long contactId = queryContactId(rawContactId);
1612        ContentValues values = new ContentValues();
1613
1614        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1615
1616        // If there is title without company, the title is display name.
1617        values.clear();
1618        values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1");
1619        Uri organizationUri = insertOrganization(rawContactId, values);
1620
1621        values.clear();
1622        values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1");
1623        values.putNull(Contacts.PHONETIC_NAME);
1624        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
1625        values.put(Contacts.SORT_KEY_PRIMARY, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
1626        values.put(Contacts.SORT_KEY_ALTERNATIVE, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
1627        assertStoredValues(uri, values);
1628    }
1629
1630    public void testLookupByOrganization() {
1631        long rawContactId = createRawContact();
1632        long contactId = queryContactId(rawContactId);
1633        ContentValues values = new ContentValues();
1634
1635        values.clear();
1636        values.put(Organization.COMPANY, "acmecorp");
1637        values.put(Organization.TITLE, "president");
1638        Uri organizationUri = insertOrganization(rawContactId, values);
1639
1640        assertContactFilter(contactId, "acmecorp");
1641        assertContactFilter(contactId, "president");
1642
1643        values.clear();
1644        values.put(Organization.DEPARTMENT, "software");
1645        mResolver.update(organizationUri, values, null, null);
1646
1647        assertContactFilter(contactId, "acmecorp");
1648        assertContactFilter(contactId, "president");
1649
1650        values.clear();
1651        values.put(Organization.COMPANY, "incredibles");
1652        mResolver.update(organizationUri, values, null, null);
1653
1654        assertContactFilter(contactId, "incredibles");
1655        assertContactFilter(contactId, "president");
1656
1657        values.clear();
1658        values.put(Organization.TITLE, "director");
1659        mResolver.update(organizationUri, values, null, null);
1660
1661        assertContactFilter(contactId, "incredibles");
1662        assertContactFilter(contactId, "director");
1663
1664        values.clear();
1665        values.put(Organization.COMPANY, "monsters");
1666        values.put(Organization.TITLE, "scarer");
1667        mResolver.update(organizationUri, values, null, null);
1668
1669        assertContactFilter(contactId, "monsters");
1670        assertContactFilter(contactId, "scarer");
1671    }
1672
1673    private void assertContactFilter(long contactId, String filter) {
1674        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
1675        assertStoredValue(filterUri, Contacts._ID, contactId);
1676    }
1677
1678    public void testSearchSnippetOrganization() throws Exception {
1679        long rawContactId = createRawContactWithName();
1680        long contactId = queryContactId(rawContactId);
1681
1682        // Some random data element
1683        insertEmail(rawContactId, "inc@corp.com");
1684
1685        ContentValues values = new ContentValues();
1686        values.clear();
1687        values.put(Organization.COMPANY, "acmecorp");
1688        values.put(Organization.TITLE, "engineer");
1689        Uri organizationUri = insertOrganization(rawContactId, values);
1690
1691        // Add another matching organization
1692        values.put(Organization.COMPANY, "acmeinc");
1693        insertOrganization(rawContactId, values);
1694
1695        // Add another non-matching organization
1696        values.put(Organization.COMPANY, "corpacme");
1697        insertOrganization(rawContactId, values);
1698
1699        // And another data element
1700        insertEmail(rawContactId, "emca@corp.com", true, Email.TYPE_CUSTOM, "Custom");
1701
1702        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
1703
1704        values.clear();
1705        values.put(Contacts._ID, contactId);
1706        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(organizationUri));
1707        values.put(SearchSnippetColumns.SNIPPET_DATA1, "acmecorp");
1708        values.put(SearchSnippetColumns.SNIPPET_DATA4, "engineer");
1709        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1710        assertStoredValues(filterUri, values);
1711    }
1712
1713    public void testSearchSnippetEmail() throws Exception {
1714        long rawContactId = createRawContact();
1715        long contactId = queryContactId(rawContactId);
1716        ContentValues values = new ContentValues();
1717
1718        Uri dataUri = insertEmail(rawContactId, "acme@corp.com", true, Email.TYPE_CUSTOM, "Custom");
1719
1720        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
1721
1722        values.clear();
1723        values.put(Contacts._ID, contactId);
1724        values.put(SearchSnippetColumns.SNIPPET_DATA1, "acme@corp.com");
1725        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(dataUri));
1726        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Email.CONTENT_ITEM_TYPE);
1727        values.put(SearchSnippetColumns.SNIPPET_DATA2, Email.TYPE_CUSTOM);
1728        values.put(SearchSnippetColumns.SNIPPET_DATA3, "Custom");
1729        assertStoredValues(filterUri, values);
1730    }
1731
1732    public void testSearchSnippetNickname() throws Exception {
1733        long rawContactId = createRawContactWithName();
1734        long contactId = queryContactId(rawContactId);
1735        ContentValues values = new ContentValues();
1736
1737        Uri dataUri = insertNickname(rawContactId, "Incredible");
1738
1739        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("inc"));
1740
1741        values.clear();
1742        values.put(Contacts._ID, contactId);
1743        values.put(SearchSnippetColumns.SNIPPET_DATA1, "Incredible");
1744        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(dataUri));
1745        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
1746        assertStoredValues(filterUri, values);
1747    }
1748
1749    public void testDisplayNameUpdateFromStructuredNameUpdate() {
1750        long rawContactId = createRawContact();
1751        Uri nameUri = insertStructuredName(rawContactId, "Slinky", "Dog");
1752
1753        long contactId = queryContactId(rawContactId);
1754
1755        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1756        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog");
1757
1758        ContentValues values = new ContentValues();
1759        values.putNull(StructuredName.FAMILY_NAME);
1760
1761        mResolver.update(nameUri, values, null, null);
1762        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky");
1763
1764        values.putNull(StructuredName.GIVEN_NAME);
1765
1766        mResolver.update(nameUri, values, null, null);
1767        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
1768
1769        values.put(StructuredName.FAMILY_NAME, "Dog");
1770        mResolver.update(nameUri, values, null, null);
1771
1772        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog");
1773    }
1774
1775    public void testInsertDataWithContentProviderOperations() throws Exception {
1776        ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
1777                .withValues(new ContentValues())
1778                .build();
1779        ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI)
1780                .withValueBackReference(Data.RAW_CONTACT_ID, 0)
1781                .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
1782                .withValue(StructuredName.GIVEN_NAME, "John")
1783                .withValue(StructuredName.FAMILY_NAME, "Doe")
1784                .build();
1785        ContentProviderResult[] results =
1786                mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2));
1787        long contactId = queryContactId(ContentUris.parseId(results[0].uri));
1788        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1789        assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe");
1790    }
1791
1792    public void testSendToVoicemailDefault() {
1793        long rawContactId = createRawContactWithName();
1794        long contactId = queryContactId(rawContactId);
1795
1796        Cursor c = queryContact(contactId);
1797        assertTrue(c.moveToNext());
1798        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
1799        assertEquals(0, sendToVoicemail);
1800        c.close();
1801    }
1802
1803    public void testSetSendToVoicemailAndRingtone() {
1804        long rawContactId = createRawContactWithName();
1805        long contactId = queryContactId(rawContactId);
1806
1807        updateSendToVoicemailAndRingtone(contactId, true, "foo");
1808        assertSendToVoicemailAndRingtone(contactId, true, "foo");
1809        assertNetworkNotified(false);
1810
1811        updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
1812        assertSendToVoicemailAndRingtone(contactId, false, "bar");
1813        assertNetworkNotified(false);
1814    }
1815
1816    public void testSendToVoicemailAndRingtoneAfterAggregation() {
1817        long rawContactId1 = createRawContactWithName("a", "b");
1818        long contactId1 = queryContactId(rawContactId1);
1819        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
1820
1821        long rawContactId2 = createRawContactWithName("c", "d");
1822        long contactId2 = queryContactId(rawContactId2);
1823        updateSendToVoicemailAndRingtone(contactId2, true, "bar");
1824
1825        // Aggregate them
1826        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1827                rawContactId1, rawContactId2);
1828
1829        // Both contacts had "send to VM", the contact now has the same value
1830        assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar
1831    }
1832
1833    public void testDoNotSendToVoicemailAfterAggregation() {
1834        long rawContactId1 = createRawContactWithName("e", "f");
1835        long contactId1 = queryContactId(rawContactId1);
1836        updateSendToVoicemailAndRingtone(contactId1, true, null);
1837
1838        long rawContactId2 = createRawContactWithName("g", "h");
1839        long contactId2 = queryContactId(rawContactId2);
1840        updateSendToVoicemailAndRingtone(contactId2, false, null);
1841
1842        // Aggregate them
1843        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1844                rawContactId1, rawContactId2);
1845
1846        // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
1847        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null);
1848    }
1849
1850    public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
1851        long rawContactId1 = createRawContactWithName("i", "j");
1852        long contactId1 = queryContactId(rawContactId1);
1853        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
1854
1855        long rawContactId2 = createRawContactWithName("k", "l");
1856        long contactId2 = queryContactId(rawContactId2);
1857        updateSendToVoicemailAndRingtone(contactId2, false, "bar");
1858
1859        // Aggregate them
1860        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1861                rawContactId1, rawContactId2);
1862
1863        // Split them
1864        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
1865                rawContactId1, rawContactId2);
1866
1867        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo");
1868        assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
1869    }
1870
1871    public void testStatusUpdateInsert() {
1872        long rawContactId = createRawContact();
1873        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1874        long dataId = ContentUris.parseId(imUri);
1875
1876        ContentValues values = new ContentValues();
1877        values.put(StatusUpdates.DATA_ID, dataId);
1878        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
1879        values.putNull(StatusUpdates.CUSTOM_PROTOCOL);
1880        values.put(StatusUpdates.IM_HANDLE, "aim");
1881        values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE);
1882        values.put(StatusUpdates.STATUS, "Hiding");
1883        values.put(StatusUpdates.STATUS_TIMESTAMP, 100);
1884        values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c");
1885        values.put(StatusUpdates.STATUS_ICON, 1234);
1886        values.put(StatusUpdates.STATUS_LABEL, 2345);
1887
1888        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
1889
1890        assertStoredValues(resultUri, values);
1891
1892        long contactId = queryContactId(rawContactId);
1893        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1894
1895        values.clear();
1896        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1897        values.put(Contacts.CONTACT_STATUS, "Hiding");
1898        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
1899        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c");
1900        values.put(Contacts.CONTACT_STATUS_ICON, 1234);
1901        values.put(Contacts.CONTACT_STATUS_LABEL, 2345);
1902
1903        assertStoredValues(contactUri, values);
1904
1905        values.clear();
1906        values.put(StatusUpdates.DATA_ID, dataId);
1907        values.put(StatusUpdates.STATUS, "Cloaked");
1908        values.put(StatusUpdates.STATUS_TIMESTAMP, 200);
1909        values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f");
1910        values.put(StatusUpdates.STATUS_ICON, 4321);
1911        values.put(StatusUpdates.STATUS_LABEL, 5432);
1912        mResolver.insert(StatusUpdates.CONTENT_URI, values);
1913
1914        values.clear();
1915        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1916        values.put(Contacts.CONTACT_STATUS, "Cloaked");
1917        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200);
1918        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f");
1919        values.put(Contacts.CONTACT_STATUS_ICON, 4321);
1920        values.put(Contacts.CONTACT_STATUS_LABEL, 5432);
1921        assertStoredValues(contactUri, values);
1922    }
1923
1924    public void testStatusUpdateInferAttribution() {
1925        long rawContactId = createRawContact();
1926        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1927        long dataId = ContentUris.parseId(imUri);
1928
1929        ContentValues values = new ContentValues();
1930        values.put(StatusUpdates.DATA_ID, dataId);
1931        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
1932        values.put(StatusUpdates.IM_HANDLE, "aim");
1933        values.put(StatusUpdates.STATUS, "Hiding");
1934
1935        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
1936
1937        values.clear();
1938        values.put(StatusUpdates.DATA_ID, dataId);
1939        values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim);
1940        values.put(StatusUpdates.STATUS, "Hiding");
1941
1942        assertStoredValues(resultUri, values);
1943    }
1944
1945    public void testStatusUpdateMatchingImOrEmail() {
1946        long rawContactId = createRawContact();
1947        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1948        insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im");
1949        insertEmail(rawContactId, "m@acme.com");
1950
1951        // Match on IM (standard)
1952        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
1953                StatusUpdates.CAPABILITY_HAS_CAMERA);
1954
1955        // Match on IM (custom)
1956        insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle",
1957                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
1958
1959        // Match on Email
1960        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m@acme.com", StatusUpdates.AWAY, "Away",
1961                StatusUpdates.CAPABILITY_HAS_VOICE);
1962
1963        // No match
1964        insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away",
1965                StatusUpdates.CAPABILITY_HAS_CAMERA);
1966
1967        Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] {
1968                StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL,
1969                StatusUpdates.PRESENCE, StatusUpdates.STATUS},
1970                PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID);
1971        assertTrue(c.moveToNext());
1972        assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available");
1973        assertTrue(c.moveToNext());
1974        assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle");
1975        assertTrue(c.moveToNext());
1976        assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away");
1977        assertFalse(c.moveToNext());
1978        c.close();
1979
1980        long contactId = queryContactId(rawContactId);
1981        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1982
1983        ContentValues values = new ContentValues();
1984        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
1985        values.put(Contacts.CONTACT_STATUS, "Available");
1986        assertStoredValuesWithProjection(contactUri, values);
1987    }
1988
1989    public void testStatusUpdateUpdateAndDelete() {
1990        long rawContactId = createRawContact();
1991        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1992
1993        long contactId = queryContactId(rawContactId);
1994        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1995
1996        ContentValues values = new ContentValues();
1997        values.putNull(Contacts.CONTACT_PRESENCE);
1998        values.putNull(Contacts.CONTACT_STATUS);
1999        assertStoredValuesWithProjection(contactUri, values);
2000
2001        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY",
2002                StatusUpdates.CAPABILITY_HAS_CAMERA);
2003        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY",
2004                StatusUpdates.CAPABILITY_HAS_CAMERA);
2005        Uri statusUri =
2006            insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
2007                    StatusUpdates.CAPABILITY_HAS_CAMERA);
2008        long statusId = ContentUris.parseId(statusUri);
2009
2010        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
2011        values.put(Contacts.CONTACT_STATUS, "Available");
2012        assertStoredValuesWithProjection(contactUri, values);
2013
2014        // update status_updates table to set new values for
2015        //     status_updates.status
2016        //     status_updates.status_ts
2017        //     presence
2018        long updatedTs = 200;
2019        String testUpdate = "test_update";
2020        String selection = StatusUpdates.DATA_ID + "=" + statusId;
2021        values.clear();
2022        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2023        values.put(StatusUpdates.STATUS, testUpdate);
2024        values.put(StatusUpdates.PRESENCE, "presence_test");
2025        mResolver.update(StatusUpdates.CONTENT_URI, values,
2026                StatusUpdates.DATA_ID + "=" + statusId, null);
2027        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2028
2029        // update status_updates table to set new values for columns in status_updates table ONLY
2030        // i.e., no rows in presence table are to be updated.
2031        updatedTs = 300;
2032        testUpdate = "test_update_new";
2033        selection = StatusUpdates.DATA_ID + "=" + statusId;
2034        values.clear();
2035        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2036        values.put(StatusUpdates.STATUS, testUpdate);
2037        mResolver.update(StatusUpdates.CONTENT_URI, values,
2038                StatusUpdates.DATA_ID + "=" + statusId, null);
2039        // make sure the presence column value is still the old value
2040        values.put(StatusUpdates.PRESENCE, "presence_test");
2041        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2042
2043        // update status_updates table to set new values for columns in presence table ONLY
2044        // i.e., no rows in status_updates table are to be updated.
2045        selection = StatusUpdates.DATA_ID + "=" + statusId;
2046        values.clear();
2047        values.put(StatusUpdates.PRESENCE, "presence_test_new");
2048        mResolver.update(StatusUpdates.CONTENT_URI, values,
2049                StatusUpdates.DATA_ID + "=" + statusId, null);
2050        // make sure the status_updates table is not updated
2051        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2052        values.put(StatusUpdates.STATUS, testUpdate);
2053        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2054
2055        // effect "delete status_updates" operation and expect the following
2056        //   data deleted from status_updates table
2057        //   presence set to null
2058        mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null);
2059        values.clear();
2060        values.putNull(Contacts.CONTACT_PRESENCE);
2061        assertStoredValuesWithProjection(contactUri, values);
2062    }
2063
2064    public void testStatusUpdateWithTimestamp() {
2065        long rawContactId = createRawContact();
2066        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2067        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
2068
2069        long contactId = queryContactId(rawContactId);
2070        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2071        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80,
2072                StatusUpdates.CAPABILITY_HAS_CAMERA);
2073        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100,
2074                StatusUpdates.CAPABILITY_HAS_CAMERA);
2075        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90,
2076                StatusUpdates.CAPABILITY_HAS_CAMERA);
2077
2078        // Should return the latest status
2079        ContentValues values = new ContentValues();
2080        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
2081        values.put(Contacts.CONTACT_STATUS, "Available");
2082        assertStoredValuesWithProjection(contactUri, values);
2083    }
2084
2085    private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence,
2086            String status) {
2087        ContentValues values = new ContentValues();
2088        values.put(StatusUpdates.PROTOCOL, protocol);
2089        values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
2090        values.put(StatusUpdates.PRESENCE, presence);
2091        values.put(StatusUpdates.STATUS, status);
2092        assertCursorValues(c, values);
2093    }
2094
2095    public void testSingleStatusUpdateRowPerContact() {
2096        int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
2097        String handle1 = "test@gmail.com";
2098
2099        long rawContactId1 = createRawContact();
2100        insertImHandle(rawContactId1, protocol1, null, handle1);
2101
2102        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green",
2103                StatusUpdates.CAPABILITY_HAS_CAMERA);
2104        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow",
2105                StatusUpdates.CAPABILITY_HAS_CAMERA);
2106        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red",
2107                StatusUpdates.CAPABILITY_HAS_CAMERA);
2108
2109        Cursor c = queryContact(queryContactId(rawContactId1),
2110                new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS});
2111        assertEquals(1, c.getCount());
2112
2113        c.moveToFirst();
2114        assertEquals(StatusUpdates.INVISIBLE, c.getInt(0));
2115        assertEquals("Red", c.getString(1));
2116        c.close();
2117    }
2118
2119    private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail,
2120            String ringtone) {
2121        ContentValues values = new ContentValues();
2122        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
2123        if (ringtone != null) {
2124            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
2125        }
2126
2127        final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2128        int count = mResolver.update(uri, values, null, null);
2129        assertEquals(1, count);
2130    }
2131
2132    private void updateSendToVoicemailAndRingtoneWithSelection(long contactId,
2133            boolean sendToVoicemail, String ringtone) {
2134        ContentValues values = new ContentValues();
2135        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
2136        if (ringtone != null) {
2137            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
2138        }
2139
2140        int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId,
2141                null);
2142        assertEquals(1, count);
2143    }
2144
2145    private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail,
2146            String expectedRingtone) {
2147        Cursor c = queryContact(contactId);
2148        assertTrue(c.moveToNext());
2149        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
2150        assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
2151        String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE));
2152        if (expectedRingtone == null) {
2153            assertNull(ringtone);
2154        } else {
2155            assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
2156        }
2157        c.close();
2158    }
2159
2160    public void testGroupCreationAfterMembershipInsert() {
2161        long rawContactId1 = createRawContact(mAccount);
2162        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
2163
2164        long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
2165        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
2166                rawContactId1, groupId, "gsid1");
2167    }
2168
2169    public void testGroupReuseAfterMembershipInsert() {
2170        long rawContactId1 = createRawContact(mAccount);
2171        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2172        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
2173
2174        assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
2175        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
2176                rawContactId1, groupId1, "gsid1");
2177    }
2178
2179    public void testGroupInsertFailureOnGroupIdConflict() {
2180        long rawContactId1 = createRawContact(mAccount);
2181        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2182
2183        ContentValues values = new ContentValues();
2184        values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1);
2185        values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
2186        values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
2187        values.put(GroupMembership.GROUP_ROW_ID, groupId1);
2188        try {
2189            mResolver.insert(Data.CONTENT_URI, values);
2190            fail("the insert was expected to fail, but it succeeded");
2191        } catch (IllegalArgumentException e) {
2192            // this was expected
2193        }
2194    }
2195
2196    public void testContactVisibilityUpdateOnMembershipChange() {
2197        long rawContactId = createRawContact(mAccount);
2198        assertVisibility(rawContactId, "0");
2199
2200        long visibleGroupId = createGroup(mAccount, "123", "Visible", 1);
2201        long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0);
2202
2203        Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId);
2204        assertVisibility(rawContactId, "1");
2205
2206        Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId);
2207        assertVisibility(rawContactId, "1");
2208
2209        mResolver.delete(membership1, null, null);
2210        assertVisibility(rawContactId, "0");
2211
2212        ContentValues values = new ContentValues();
2213        values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId);
2214
2215        mResolver.update(membership2, values, null, null);
2216        assertVisibility(rawContactId, "1");
2217    }
2218
2219    private void assertVisibility(long rawContactId, String expectedValue) {
2220        assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId),
2221                null, Contacts.IN_VISIBLE_GROUP, expectedValue);
2222    }
2223
2224    public void testContentEntityIterator() {
2225        // create multiple contacts and check that the selected ones are returned
2226        long id;
2227
2228        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2229        long groupId2 = createGroup(mAccount, "gsid2", "title2");
2230
2231        id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c0");
2232        insertGroupMembership(id, "gsid1");
2233        insertEmail(id, "c0@email.com");
2234        insertPhoneNumber(id, "5551212c0");
2235
2236        long c1 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c1");
2237        Uri id_1_0 = insertGroupMembership(id, "gsid1");
2238        Uri id_1_1 = insertGroupMembership(id, "gsid2");
2239        Uri id_1_2 = insertEmail(id, "c1@email.com");
2240        Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
2241
2242        long c2 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c2");
2243        Uri id_2_0 = insertGroupMembership(id, "gsid1");
2244        Uri id_2_1 = insertEmail(id, "c2@email.com");
2245        Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
2246
2247        long c3 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c3");
2248        Uri id_3_0 = insertGroupMembership(id, groupId2);
2249        Uri id_3_1 = insertEmail(id, "c3@email.com");
2250        Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
2251
2252        EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query(
2253                maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), null,
2254                RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null));
2255        Entity entity;
2256        ContentValues[] subValues;
2257        entity = iterator.next();
2258        assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2259        subValues = asSortedContentValuesArray(entity.getSubValues());
2260        assertEquals(4, subValues.length);
2261        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2262                Data._ID, id_1_0,
2263                GroupMembership.GROUP_ROW_ID, groupId1,
2264                GroupMembership.GROUP_SOURCE_ID, "gsid1");
2265        assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
2266                Data._ID, id_1_1,
2267                GroupMembership.GROUP_ROW_ID, groupId2,
2268                GroupMembership.GROUP_SOURCE_ID, "gsid2");
2269        assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
2270                Data._ID, id_1_2,
2271                Email.DATA, "c1@email.com");
2272        assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
2273                Data._ID, id_1_3,
2274                Email.DATA, "5551212c1");
2275
2276        entity = iterator.next();
2277        assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2278        subValues = asSortedContentValuesArray(entity.getSubValues());
2279        assertEquals(3, subValues.length);
2280        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2281                Data._ID, id_2_0,
2282                GroupMembership.GROUP_ROW_ID, groupId1,
2283                GroupMembership.GROUP_SOURCE_ID, "gsid1");
2284        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
2285                Data._ID, id_2_1,
2286                Email.DATA, "c2@email.com");
2287        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
2288                Data._ID, id_2_2,
2289                Email.DATA, "5551212c2");
2290
2291        entity = iterator.next();
2292        assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2293        subValues = asSortedContentValuesArray(entity.getSubValues());
2294        assertEquals(3, subValues.length);
2295        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2296                Data._ID, id_3_0,
2297                GroupMembership.GROUP_ROW_ID, groupId2,
2298                GroupMembership.GROUP_SOURCE_ID, "gsid2");
2299        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
2300                Data._ID, id_3_1,
2301                Email.DATA, "c3@email.com");
2302        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
2303                Data._ID, id_3_2,
2304                Email.DATA, "5551212c3");
2305
2306        assertFalse(iterator.hasNext());
2307        iterator.close();
2308    }
2309
2310    public void testDataCreateUpdateDeleteByMimeType() throws Exception {
2311        long rawContactId = createRawContact();
2312
2313        ContentValues values = new ContentValues();
2314        values.put(Data.RAW_CONTACT_ID, rawContactId);
2315        values.put(Data.MIMETYPE, "testmimetype");
2316        values.put(Data.RES_PACKAGE, "oldpackage");
2317        values.put(Data.IS_PRIMARY, 1);
2318        values.put(Data.IS_SUPER_PRIMARY, 1);
2319        values.put(Data.DATA1, "old1");
2320        values.put(Data.DATA2, "old2");
2321        values.put(Data.DATA3, "old3");
2322        values.put(Data.DATA4, "old4");
2323        values.put(Data.DATA5, "old5");
2324        values.put(Data.DATA6, "old6");
2325        values.put(Data.DATA7, "old7");
2326        values.put(Data.DATA8, "old8");
2327        values.put(Data.DATA9, "old9");
2328        values.put(Data.DATA10, "old10");
2329        values.put(Data.DATA11, "old11");
2330        values.put(Data.DATA12, "old12");
2331        values.put(Data.DATA13, "old13");
2332        values.put(Data.DATA14, "old14");
2333        values.put(Data.DATA15, "old15");
2334        Uri uri = mResolver.insert(Data.CONTENT_URI, values);
2335        assertStoredValues(uri, values);
2336        assertNetworkNotified(true);
2337
2338        values.clear();
2339        values.put(Data.RES_PACKAGE, "newpackage");
2340        values.put(Data.IS_PRIMARY, 0);
2341        values.put(Data.IS_SUPER_PRIMARY, 0);
2342        values.put(Data.DATA1, "new1");
2343        values.put(Data.DATA2, "new2");
2344        values.put(Data.DATA3, "new3");
2345        values.put(Data.DATA4, "new4");
2346        values.put(Data.DATA5, "new5");
2347        values.put(Data.DATA6, "new6");
2348        values.put(Data.DATA7, "new7");
2349        values.put(Data.DATA8, "new8");
2350        values.put(Data.DATA9, "new9");
2351        values.put(Data.DATA10, "new10");
2352        values.put(Data.DATA11, "new11");
2353        values.put(Data.DATA12, "new12");
2354        values.put(Data.DATA13, "new13");
2355        values.put(Data.DATA14, "new14");
2356        values.put(Data.DATA15, "new15");
2357        mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId +
2358                " AND " + Data.MIMETYPE + "='testmimetype'", null);
2359        assertNetworkNotified(true);
2360
2361        // Should not be able to change IS_PRIMARY and IS_SUPER_PRIMARY by the above update
2362        values.put(Data.IS_PRIMARY, 1);
2363        values.put(Data.IS_SUPER_PRIMARY, 1);
2364        assertStoredValues(uri, values);
2365
2366        int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
2367                + " AND " + Data.MIMETYPE + "='testmimetype'", null);
2368        assertEquals(1, count);
2369        assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
2370                        + " AND " + Data.MIMETYPE + "='testmimetype'", null));
2371        assertNetworkNotified(true);
2372    }
2373
2374    public void testRawContactQuery() {
2375        Account account1 = new Account("a", "b");
2376        Account account2 = new Account("c", "d");
2377        long rawContactId1 = createRawContact(account1);
2378        long rawContactId2 = createRawContact(account2);
2379
2380        Uri uri1 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1);
2381        Uri uri2 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2);
2382        assertEquals(1, getCount(uri1, null, null));
2383        assertEquals(1, getCount(uri2, null, null));
2384        assertStoredValue(uri1, RawContacts._ID, rawContactId1) ;
2385        assertStoredValue(uri2, RawContacts._ID, rawContactId2) ;
2386
2387        Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1);
2388        Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2);
2389        assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ;
2390        assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ;
2391    }
2392
2393    public void testRawContactDeletion() {
2394        long rawContactId = createRawContact(mAccount);
2395        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
2396
2397        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
2398        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
2399                StatusUpdates.AVAILABLE, null,
2400                StatusUpdates.CAPABILITY_HAS_CAMERA);
2401        long contactId = queryContactId(rawContactId);
2402
2403        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
2404                null, null));
2405        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
2406                + rawContactId, null));
2407
2408        mResolver.delete(uri, null, null);
2409
2410        assertStoredValue(uri, RawContacts.DELETED, "1");
2411        assertNetworkNotified(true);
2412
2413        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
2414        mResolver.delete(permanentDeletionUri, null, null);
2415        assertEquals(0, getCount(uri, null, null));
2416        assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
2417                null, null));
2418        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
2419                + rawContactId, null));
2420        assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
2421        assertNetworkNotified(false);
2422    }
2423
2424    public void testRawContactDeletionKeepingAggregateContact() {
2425        long rawContactId1 = createRawContactWithName(mAccount);
2426        long rawContactId2 = createRawContactWithName(mAccount);
2427
2428        // Same name - should be aggregated
2429        assertAggregated(rawContactId1, rawContactId2);
2430
2431        long contactId = queryContactId(rawContactId1);
2432
2433        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
2434        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
2435        mResolver.delete(permanentDeletionUri, null, null);
2436        assertEquals(0, getCount(uri, null, null));
2437        assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
2438    }
2439
2440    public void testRawContactDeletionWithAccounts() {
2441        long rawContactId = createRawContact(mAccount);
2442        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
2443
2444        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
2445        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
2446                StatusUpdates.AVAILABLE, null,
2447                StatusUpdates.CAPABILITY_HAS_CAMERA);
2448        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
2449                null, null));
2450        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
2451                + rawContactId, null));
2452
2453        // Do not delete if we are deleting with wrong account.
2454        Uri deleteWithWrongAccountUri =
2455            RawContacts.CONTENT_URI.buildUpon()
2456                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name)
2457                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type)
2458                .build();
2459        mResolver.delete(deleteWithWrongAccountUri, null, null);
2460
2461        assertStoredValue(uri, RawContacts.DELETED, "0");
2462
2463        // Delete if we are deleting with correct account.
2464        Uri deleteWithCorrectAccountUri =
2465            RawContacts.CONTENT_URI.buildUpon()
2466                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name)
2467                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type)
2468                .build();
2469        mResolver.delete(deleteWithCorrectAccountUri, null, null);
2470
2471        assertStoredValue(uri, RawContacts.DELETED, "1");
2472    }
2473
2474    public void testAccountsUpdated() {
2475        // This is to ensure we do not delete contacts with null, null (account name, type)
2476        // accidentally.
2477        long rawContactId3 = createRawContactWithName("James", "Sullivan");
2478        insertPhoneNumber(rawContactId3, "5234567890");
2479        Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3);
2480        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
2481
2482        ContactsProvider2 cp = (ContactsProvider2) getProvider();
2483        cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo});
2484        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
2485        assertStoredValue(rawContact3, RawContacts.ACCOUNT_NAME, null);
2486        assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, null);
2487
2488        long rawContactId1 = createRawContact(mAccount);
2489        insertEmail(rawContactId1, "account1@email.com");
2490        long rawContactId2 = createRawContact(mAccountTwo);
2491        insertEmail(rawContactId2, "account2@email.com");
2492        insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
2493        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
2494                StatusUpdates.AVAILABLE, null,
2495                StatusUpdates.CAPABILITY_HAS_CAMERA);
2496
2497        cp.onAccountsUpdated(new Account[]{mAccount});
2498        assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null));
2499        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
2500                + rawContactId2, null));
2501    }
2502
2503    public void testAccountDeletion() {
2504        Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE);
2505        ContactsProvider2 cp = (ContactsProvider2) getProvider();
2506        cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount});
2507
2508        long rawContactId1 = createRawContactWithName("John", "Doe", readOnlyAccount);
2509        Uri photoUri1 = insertPhoto(rawContactId1);
2510        long rawContactId2 = createRawContactWithName("john", "doe", mAccount);
2511        Uri photoUri2 = insertPhoto(rawContactId2);
2512        storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1");
2513
2514        assertAggregated(rawContactId1, rawContactId2);
2515
2516        long contactId = queryContactId(rawContactId1);
2517
2518        // The display name should come from the writable account
2519        assertStoredValue(Uri.withAppendedPath(
2520                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
2521                Contacts.Data.CONTENT_DIRECTORY),
2522                Contacts.DISPLAY_NAME, "john doe");
2523
2524        // The photo should be the one we marked as super-primary
2525        assertStoredValue(Contacts.CONTENT_URI, contactId,
2526                Contacts.PHOTO_ID, ContentUris.parseId(photoUri2));
2527
2528        // Remove the writable account
2529        cp.onAccountsUpdated(new Account[]{readOnlyAccount});
2530
2531        // The display name should come from the remaining account
2532        assertStoredValue(Uri.withAppendedPath(
2533                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
2534                Contacts.Data.CONTENT_DIRECTORY),
2535                Contacts.DISPLAY_NAME, "John Doe");
2536
2537        // The photo should be the remaining one
2538        assertStoredValue(Contacts.CONTENT_URI, contactId,
2539                Contacts.PHOTO_ID, ContentUris.parseId(photoUri1));
2540
2541    }
2542
2543    public void testContactDeletion() {
2544        long rawContactId1 = createRawContactWithName("John", "Doe");
2545        long rawContactId2 = createRawContactWithName("John", "Doe");
2546
2547        long contactId = queryContactId(rawContactId1);
2548
2549        mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null);
2550
2551        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
2552                RawContacts.DELETED, "1");
2553        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
2554                RawContacts.DELETED, "1");
2555    }
2556
2557    public void testMarkAsDirtyParameter() {
2558        long rawContactId = createRawContact(mAccount);
2559        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
2560
2561        Uri uri = insertStructuredName(rawContactId, "John", "Doe");
2562        clearDirty(rawContactUri);
2563        Uri updateUri = setCallerIsSyncAdapter(uri, mAccount);
2564
2565        ContentValues values = new ContentValues();
2566        values.put(StructuredName.FAMILY_NAME, "Dough");
2567        mResolver.update(updateUri, values, null, null);
2568        assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough");
2569        assertDirty(rawContactUri, false);
2570        assertNetworkNotified(false);
2571    }
2572
2573    public void testRawContactDirtyAndVersion() {
2574        final long rawContactId = createRawContact(mAccount);
2575        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
2576        assertDirty(uri, false);
2577        long version = getVersion(uri);
2578
2579        ContentValues values = new ContentValues();
2580        values.put(ContactsContract.RawContacts.DIRTY, 0);
2581        values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1);
2582        values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
2583                RawContacts.AGGREGATION_MODE_IMMEDIATE);
2584        values.put(ContactsContract.RawContacts.STARRED, 1);
2585        assertEquals(1, mResolver.update(uri, values, null, null));
2586        assertEquals(version, getVersion(uri));
2587
2588        assertDirty(uri, false);
2589        assertNetworkNotified(false);
2590
2591        Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
2592        assertDirty(uri, true);
2593        assertNetworkNotified(true);
2594        ++version;
2595        assertEquals(version, getVersion(uri));
2596        clearDirty(uri);
2597
2598        values = new ContentValues();
2599        values.put(Email.DATA, "goo@hoo.com");
2600        mResolver.update(emailUri, values, null, null);
2601        assertDirty(uri, true);
2602        assertNetworkNotified(true);
2603        ++version;
2604        assertEquals(version, getVersion(uri));
2605        clearDirty(uri);
2606
2607        mResolver.delete(emailUri, null, null);
2608        assertDirty(uri, true);
2609        assertNetworkNotified(true);
2610        ++version;
2611        assertEquals(version, getVersion(uri));
2612    }
2613
2614    public void testRawContactClearDirty() {
2615        final long rawContactId = createRawContact(mAccount);
2616        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
2617                rawContactId);
2618        long version = getVersion(uri);
2619        insertEmail(rawContactId, "goo@woo.com");
2620        assertDirty(uri, true);
2621        version++;
2622        assertEquals(version, getVersion(uri));
2623
2624        clearDirty(uri);
2625        assertDirty(uri, false);
2626        assertEquals(version, getVersion(uri));
2627    }
2628
2629    public void testRawContactDeletionSetsDirty() {
2630        final long rawContactId = createRawContact(mAccount);
2631        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
2632                rawContactId);
2633        long version = getVersion(uri);
2634        clearDirty(uri);
2635        assertDirty(uri, false);
2636
2637        mResolver.delete(uri, null, null);
2638        assertStoredValue(uri, RawContacts.DELETED, "1");
2639        assertDirty(uri, true);
2640        assertNetworkNotified(true);
2641        version++;
2642        assertEquals(version, getVersion(uri));
2643    }
2644
2645    public void testDeleteContactWithoutName() {
2646        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
2647        long rawContactId = ContentUris.parseId(rawContactUri);
2648
2649        Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true);
2650
2651        long contactId = queryContactId(rawContactId);
2652        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2653        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
2654
2655        int numDeleted = mResolver.delete(lookupUri, null, null);
2656        assertEquals(1, numDeleted);
2657    }
2658
2659    public void testDeleteContactWithoutAnyData() {
2660        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
2661        long rawContactId = ContentUris.parseId(rawContactUri);
2662
2663        long contactId = queryContactId(rawContactId);
2664        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2665        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
2666
2667        int numDeleted = mResolver.delete(lookupUri, null, null);
2668        assertEquals(1, numDeleted);
2669    }
2670
2671    public void testDeleteContactWithEscapedUri() {
2672        ContentValues values = new ContentValues();
2673        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
2674        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2675        long rawContactId = ContentUris.parseId(rawContactUri);
2676
2677        long contactId = queryContactId(rawContactId);
2678        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2679        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
2680        assertEquals(1, mResolver.delete(lookupUri, null, null));
2681    }
2682
2683    public void testQueryContactWithEscapedUri() {
2684        ContentValues values = new ContentValues();
2685        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
2686        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2687        long rawContactId = ContentUris.parseId(rawContactUri);
2688
2689        long contactId = queryContactId(rawContactId);
2690        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2691        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
2692        Cursor c = mResolver.query(lookupUri, null, null, null, "");
2693        assertEquals(1, c.getCount());
2694        c.close();
2695    }
2696
2697    public void testGetPhotoUri() {
2698        ContentValues values = new ContentValues();
2699        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2700        long rawContactId = ContentUris.parseId(rawContactUri);
2701        insertStructuredName(rawContactId, "John", "Doe");
2702        Uri photoUri = insertPhoto(rawContactId);
2703
2704        Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
2705                queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
2706
2707        long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
2708        assertEquals(ContentUris.parseId(photoUri), twigId);
2709    }
2710
2711    public void testInputStreamForPhoto() throws Exception {
2712        long rawContactId = createRawContact();
2713        Uri photoUri = insertPhoto(rawContactId);
2714        assertInputStreamContent(loadTestPhoto(), mResolver.openInputStream(photoUri));
2715
2716        Uri contactPhotoUri = Uri.withAppendedPath(
2717                ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
2718                Contacts.Photo.CONTENT_DIRECTORY);
2719        assertInputStreamContent(loadTestPhoto(), mResolver.openInputStream(contactPhotoUri));
2720    }
2721
2722    private static void assertInputStreamContent(byte[] expected, InputStream is)
2723            throws IOException {
2724        try {
2725            byte[] observed = new byte[expected.length];
2726            int count = is.read(observed);
2727            assertEquals(expected.length, count);
2728            assertEquals(-1, is.read());
2729            MoreAsserts.assertEquals(expected, observed);
2730        } finally {
2731            is.close();
2732        }
2733    }
2734
2735    public void testSuperPrimaryPhoto() {
2736        long rawContactId1 = createRawContact(new Account("a", "a"));
2737        Uri photoUri1 = insertPhoto(rawContactId1);
2738        long photoId1 = ContentUris.parseId(photoUri1);
2739
2740        long rawContactId2 = createRawContact(new Account("b", "b"));
2741        Uri photoUri2 = insertPhoto(rawContactId2);
2742        long photoId2 = ContentUris.parseId(photoUri2);
2743
2744        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2745                rawContactId1, rawContactId2);
2746
2747        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2748                queryContactId(rawContactId1));
2749        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
2750
2751        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
2752                rawContactId1, rawContactId2);
2753
2754        ContentValues values = new ContentValues();
2755        values.put(Data.IS_SUPER_PRIMARY, 1);
2756        mResolver.update(photoUri2, values, null, null);
2757
2758        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2759                rawContactId1, rawContactId2);
2760        contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2761                queryContactId(rawContactId1));
2762        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2);
2763
2764        mResolver.update(photoUri1, values, null, null);
2765        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
2766    }
2767
2768    public void testUpdatePhoto() {
2769        ContentValues values = new ContentValues();
2770        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2771        long rawContactId = ContentUris.parseId(rawContactUri);
2772        insertStructuredName(rawContactId, "John", "Doe");
2773
2774        Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
2775                queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
2776
2777        values.clear();
2778        values.put(Data.RAW_CONTACT_ID, rawContactId);
2779        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2780        values.putNull(Photo.PHOTO);
2781        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
2782        long photoId = ContentUris.parseId(dataUri);
2783
2784        assertNull(getStoredValue(twigUri, Data._ID));
2785
2786        values.clear();
2787        values.put(Photo.PHOTO, loadTestPhoto());
2788        mResolver.update(dataUri, values, null, null);
2789        assertNetworkNotified(true);
2790
2791        long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
2792        assertEquals(photoId, twigId);
2793    }
2794
2795    public void testUpdateRawContactDataPhoto() {
2796        // setup a contact with a null photo
2797        ContentValues values = new ContentValues();
2798        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2799        long rawContactId = ContentUris.parseId(rawContactUri);
2800
2801        // setup a photo
2802        values.put(Data.RAW_CONTACT_ID, rawContactId);
2803        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2804        values.putNull(Photo.PHOTO);
2805
2806        // try to do an update before insert should return count == 0
2807        Uri dataUri = Uri.withAppendedPath(
2808                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
2809                RawContacts.Data.CONTENT_DIRECTORY);
2810        assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
2811                new String[] {Photo.CONTENT_ITEM_TYPE}));
2812
2813        mResolver.insert(Data.CONTENT_URI, values);
2814
2815        // save a photo to the db
2816        values.clear();
2817        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2818        values.put(Photo.PHOTO, loadTestPhoto());
2819        assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
2820                new String[] {Photo.CONTENT_ITEM_TYPE}));
2821
2822        // verify the photo
2823        Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
2824                Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
2825        storedPhoto.moveToFirst();
2826        MoreAsserts.assertEquals(loadTestPhoto(), storedPhoto.getBlob(0));
2827        storedPhoto.close();
2828    }
2829
2830    public void testUpdateRawContactSetStarred() {
2831        long rawContactId1 = createRawContactWithName();
2832        Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
2833        long rawContactId2 = createRawContactWithName();
2834        Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
2835
2836        assertAggregated(rawContactId1, rawContactId2);
2837
2838        long contactId = queryContactId(rawContactId1);
2839        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2840        assertStoredValue(contactUri, Contacts.STARRED, "0");
2841
2842        ContentValues values = new ContentValues();
2843        values.put(RawContacts.STARRED, "1");
2844
2845        mResolver.update(rawContactUri1, values, null, null);
2846
2847        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
2848        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
2849        assertStoredValue(contactUri, Contacts.STARRED, "1");
2850
2851        values.put(RawContacts.STARRED, "0");
2852        mResolver.update(rawContactUri1, values, null, null);
2853
2854        assertStoredValue(rawContactUri1, RawContacts.STARRED, "0");
2855        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
2856        assertStoredValue(contactUri, Contacts.STARRED, "0");
2857
2858        values.put(Contacts.STARRED, "1");
2859        mResolver.update(contactUri, values, null, null);
2860
2861        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
2862        assertStoredValue(rawContactUri2, RawContacts.STARRED, "1");
2863        assertStoredValue(contactUri, Contacts.STARRED, "1");
2864    }
2865
2866    public void testLiveFolders() {
2867        long rawContactId1 = createRawContactWithName("James", "Sullivan");
2868        insertPhoneNumber(rawContactId1, "5234567890");
2869        long contactId1 = queryContactId(rawContactId1);
2870
2871        long rawContactId2 = createRawContactWithName("Mike", "Wazowski");
2872        long contactId2 = queryContactId(rawContactId2);
2873        storeValue(Contacts.CONTENT_URI, contactId2, Contacts.STARRED, "1");
2874
2875        long rawContactId3 = createRawContactWithName("Randall", "Boggs");
2876        long contactId3 = queryContactId(rawContactId3);
2877        long groupId = createGroup(NO_ACCOUNT, "src1", "VIP");
2878        insertGroupMembership(rawContactId3, groupId);
2879
2880        assertLiveFolderContents(
2881                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2882                        "live_folders/contacts"),
2883                contactId1, "James Sullivan",
2884                contactId2, "Mike Wazowski",
2885                contactId3, "Randall Boggs");
2886
2887        assertLiveFolderContents(
2888                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2889                        "live_folders/contacts_with_phones"),
2890                contactId1, "James Sullivan");
2891
2892        assertLiveFolderContents(
2893                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2894                        "live_folders/favorites"),
2895                contactId2, "Mike Wazowski");
2896
2897        assertLiveFolderContents(
2898                Uri.withAppendedPath(Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2899                        "live_folders/contacts"), Uri.encode("VIP")),
2900                contactId3, "Randall Boggs");
2901    }
2902
2903    private void assertLiveFolderContents(Uri uri, Object... expected) {
2904        Cursor c = mResolver.query(uri, new String[]{LiveFolders._ID, LiveFolders.NAME},
2905                null, null, LiveFolders._ID);
2906        assertEquals(expected.length/2, c.getCount());
2907        for (int i = 0; i < expected.length/2; i++) {
2908            assertTrue(c.moveToNext());
2909            assertEquals(((Long)expected[i * 2]).longValue(), c.getLong(0));
2910            assertEquals(expected[i * 2 + 1], c.getString(1));
2911        }
2912        c.close();
2913    }
2914
2915    public void testContactCounts() {
2916        Uri uri = Contacts.CONTENT_URI.buildUpon()
2917                .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
2918
2919        createRawContact();
2920        createRawContactWithName("James", "Sullivan");
2921        createRawContactWithName("The Abominable", "Snowman");
2922        createRawContactWithName("Mike", "Wazowski");
2923        createRawContactWithName("randall", "boggs");
2924        createRawContactWithName("Boo", null);
2925        createRawContactWithName("Mary", null);
2926        createRawContactWithName("Roz", null);
2927
2928        Cursor cursor = mResolver.query(uri,
2929                new String[]{Contacts.DISPLAY_NAME},
2930                null, null, Contacts.SORT_KEY_PRIMARY + " COLLATE LOCALIZED");
2931
2932        assertFirstLetterValues(cursor, null, "B", "J", "M", "R", "T");
2933        assertFirstLetterCounts(cursor,    1,   1,   1,   2,   2,   1);
2934        cursor.close();
2935
2936        cursor = mResolver.query(uri,
2937                new String[]{Contacts.DISPLAY_NAME},
2938                null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC");
2939
2940        assertFirstLetterValues(cursor, "W", "S", "R", "M", "B", null);
2941        assertFirstLetterCounts(cursor,   1,   2,   1,   1,   2,    1);
2942        cursor.close();
2943    }
2944
2945    private void assertFirstLetterValues(Cursor cursor, String... expected) {
2946        String[] actual = cursor.getExtras()
2947                .getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
2948        MoreAsserts.assertEquals(expected, actual);
2949    }
2950
2951    private void assertFirstLetterCounts(Cursor cursor, int... expected) {
2952        int[] actual = cursor.getExtras()
2953                .getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
2954        MoreAsserts.assertEquals(expected, actual);
2955    }
2956
2957    public void testReadBooleanQueryParameter() {
2958        assertBooleanUriParameter("foo:bar", "bool", true, true);
2959        assertBooleanUriParameter("foo:bar", "bool", false, false);
2960        assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false);
2961        assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true);
2962        assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false);
2963        assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true);
2964        assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false);
2965        assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false);
2966        assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true);
2967        assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true);
2968        assertBooleanUriParameter("foo:bar?bool", "bool", true, true);
2969    }
2970
2971    private void assertBooleanUriParameter(String uriString, String parameter,
2972            boolean defaultValue, boolean expectedValue) {
2973        assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter(
2974                Uri.parse(uriString), parameter, defaultValue));
2975    }
2976
2977    public void testGetQueryParameter() {
2978        assertQueryParameter("foo:bar", "param", null);
2979        assertQueryParameter("foo:bar?param", "param", null);
2980        assertQueryParameter("foo:bar?param=", "param", "");
2981        assertQueryParameter("foo:bar?param=val", "param", "val");
2982        assertQueryParameter("foo:bar?param=val&some=some", "param", "val");
2983        assertQueryParameter("foo:bar?some=some&param=val", "param", "val");
2984        assertQueryParameter("foo:bar?some=some&param=val&else=else", "param", "val");
2985        assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john@doe.com");
2986    }
2987
2988    public void testMissingAccountTypeParameter() {
2989        // Try querying for RawContacts only using ACCOUNT_NAME
2990        final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(
2991                RawContacts.ACCOUNT_NAME, "lolwut").build();
2992        try {
2993            final Cursor cursor = mResolver.query(queryUri, null, null, null, null);
2994            fail("Able to query with incomplete account query parameters");
2995        } catch (IllegalArgumentException e) {
2996            // Expected behavior.
2997        }
2998    }
2999
3000    public void testInsertInconsistentAccountType() {
3001        // Try inserting RawContact with inconsistent Accounts
3002        final Account red = new Account("red", "red");
3003        final Account blue = new Account("blue", "blue");
3004
3005        final ContentValues values = new ContentValues();
3006        values.put(RawContacts.ACCOUNT_NAME, red.name);
3007        values.put(RawContacts.ACCOUNT_TYPE, red.type);
3008
3009        final Uri insertUri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, blue);
3010        try {
3011            mResolver.insert(insertUri, values);
3012            fail("Able to insert RawContact with inconsistent account details");
3013        } catch (IllegalArgumentException e) {
3014            // Expected behavior.
3015        }
3016    }
3017
3018    public void testProviderStatus() throws Exception {
3019        Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI,
3020                new String[]{ProviderStatus.DATA1, ProviderStatus.STATUS}, null, null, null);
3021        assertTrue(cursor.moveToFirst());
3022        assertEquals(0, cursor.getLong(0));
3023        assertEquals(ProviderStatus.STATUS_NORMAL, cursor.getInt(1));
3024        cursor.close();
3025    }
3026
3027    public void testProperties() throws Exception {
3028        ContactsProvider2 provider = (ContactsProvider2)getProvider();
3029        ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper();
3030        assertNull(helper.getProperty("non-existent", null));
3031        assertEquals("default", helper.getProperty("non-existent", "default"));
3032
3033        helper.setProperty("existent1", "string1");
3034        helper.setProperty("existent2", "string2");
3035        assertEquals("string1", helper.getProperty("existent1", "default"));
3036        assertEquals("string2", helper.getProperty("existent2", "default"));
3037        helper.setProperty("existent1", null);
3038        assertEquals("default", helper.getProperty("existent1", "default"));
3039    }
3040
3041    private class VCardTestUriCreator {
3042        private String mLookup1;
3043        private String mLookup2;
3044
3045        public VCardTestUriCreator(String lookup1, String lookup2) {
3046            super();
3047            mLookup1 = lookup1;
3048            mLookup2 = lookup2;
3049        }
3050
3051        public Uri getUri1() {
3052            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1);
3053        }
3054
3055        public Uri getUri2() {
3056            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2);
3057        }
3058
3059        public Uri getCombinedUri() {
3060            return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI,
3061                    Uri.encode(mLookup1 + ":" + mLookup2));
3062        }
3063    }
3064
3065    private VCardTestUriCreator createVCardTestContacts() {
3066        final long rawContactId1 = createRawContact(mAccount, RawContacts.SOURCE_ID, "4:12");
3067        insertStructuredName(rawContactId1, "John", "Doe");
3068
3069        final long rawContactId2 = createRawContact(mAccount, RawContacts.SOURCE_ID, "3:4%121");
3070        insertStructuredName(rawContactId2, "Jane", "Doh");
3071
3072        final long contactId1 = queryContactId(rawContactId1);
3073        final long contactId2 = queryContactId(rawContactId2);
3074        final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
3075        final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
3076        final String lookup1 =
3077            Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2));
3078        final String lookup2 =
3079            Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2));
3080        return new VCardTestUriCreator(lookup1, lookup2);
3081    }
3082
3083    public void testQueryMultiVCard() {
3084        // No need to create any contacts here, because the query for multiple vcards
3085        // does not go into the database at all
3086        Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456"));
3087        Cursor cursor = mResolver.query(uri, null, null, null, null);
3088        assertEquals(1, cursor.getCount());
3089        assertTrue(cursor.moveToFirst());
3090        assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
3091        String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
3092
3093        // The resulting name contains date and time. Ensure that before and after are correct
3094        assertTrue(filename.startsWith("vcards_"));
3095        assertTrue(filename.endsWith(".vcf"));
3096        cursor.close();
3097    }
3098
3099    public void testQueryFileSingleVCard() {
3100        final VCardTestUriCreator contacts = createVCardTestContacts();
3101
3102        {
3103            Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null);
3104            assertEquals(1, cursor.getCount());
3105            assertTrue(cursor.moveToFirst());
3106            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
3107            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
3108            assertEquals("John Doe.vcf", filename);
3109            cursor.close();
3110        }
3111
3112        {
3113            Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null);
3114            assertEquals(1, cursor.getCount());
3115            assertTrue(cursor.moveToFirst());
3116            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
3117            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
3118            assertEquals("Jane Doh.vcf", filename);
3119            cursor.close();
3120        }
3121    }
3122
3123
3124    public void testOpenAssetFileMultiVCard() throws IOException {
3125        final VCardTestUriCreator contacts = createVCardTestContacts();
3126
3127        final AssetFileDescriptor descriptor =
3128            mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r");
3129        final FileInputStream inputStream = descriptor.createInputStream();
3130        String data = readToEnd(inputStream);
3131        inputStream.close();
3132        descriptor.close();
3133
3134        // Ensure that the resulting VCard has both contacts
3135        assertTrue(data.contains("N:Doe;John;;;"));
3136        assertTrue(data.contains("N:Doh;Jane;;;"));
3137    }
3138
3139    public void testOpenAssetFileSingleVCard() throws IOException {
3140        final VCardTestUriCreator contacts = createVCardTestContacts();
3141
3142        // Ensure that the right VCard is being created in each case
3143        {
3144            final AssetFileDescriptor descriptor =
3145                mResolver.openAssetFileDescriptor(contacts.getUri1(), "r");
3146            final FileInputStream inputStream = descriptor.createInputStream();
3147            final String data = readToEnd(inputStream);
3148            assertTrue(data.contains("N:Doe;John;;;"));
3149            assertFalse(data.contains("N:Doh;Jane;;;"));
3150
3151            inputStream.close();
3152            descriptor.close();
3153        }
3154
3155        {
3156            final AssetFileDescriptor descriptor =
3157                mResolver.openAssetFileDescriptor(contacts.getUri2(), "r");
3158            final FileInputStream inputStream = descriptor.createInputStream();
3159            final String data = readToEnd(inputStream);
3160            inputStream.close();
3161            descriptor.close();
3162
3163            assertFalse(data.contains("N:Doe;John;;;"));
3164            assertTrue(data.contains("N:Doh;Jane;;;"));
3165        }
3166    }
3167
3168    public void testAutoGroupMembership() {
3169        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
3170        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
3171        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */);
3172        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */);
3173        long r1 = createRawContact(mAccount);
3174        long r2 = createRawContact(mAccountTwo);
3175        long r3 = createRawContact(null);
3176
3177        Cursor c = queryGroupMemberships(mAccount);
3178        try {
3179            assertTrue(c.moveToNext());
3180            assertEquals(g1, c.getLong(0));
3181            assertEquals(r1, c.getLong(1));
3182            assertFalse(c.moveToNext());
3183        } finally {
3184            c.close();
3185        }
3186
3187        c = queryGroupMemberships(mAccountTwo);
3188        try {
3189            assertTrue(c.moveToNext());
3190            assertEquals(g3, c.getLong(0));
3191            assertEquals(r2, c.getLong(1));
3192            assertFalse(c.moveToNext());
3193        } finally {
3194            c.close();
3195        }
3196    }
3197
3198    public void testNoAutoAddMembershipAfterGroupCreation() {
3199        long r1 = createRawContact(mAccount);
3200        long r2 = createRawContact(mAccount);
3201        long r3 = createRawContact(mAccount);
3202        long r4 = createRawContact(mAccountTwo);
3203        long r5 = createRawContact(mAccountTwo);
3204        long r6 = createRawContact(null);
3205
3206        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3207        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3208
3209        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
3210        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
3211        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */);
3212
3213        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3214        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3215    }
3216
3217    // create some starred and non-starred contacts, some associated with account, some not
3218    // favorites group created
3219    // the starred contacts should be added to group
3220    // favorites group removed
3221    // no change to starred status
3222    public void testFavoritesMembershipAfterGroupCreation() {
3223        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
3224        long r2 = createRawContact(mAccount);
3225        long r3 = createRawContact(mAccount, RawContacts.STARRED, "1");
3226        long r4 = createRawContact(mAccountTwo, RawContacts.STARRED, "1");
3227        long r5 = createRawContact(mAccountTwo);
3228        long r6 = createRawContact(null, RawContacts.STARRED, "1");
3229        long r7 = createRawContact(null);
3230
3231        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3232        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3233
3234        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
3235        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
3236        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */);
3237
3238        assertTrue(queryRawContactIsStarred(r1));
3239        assertFalse(queryRawContactIsStarred(r2));
3240        assertTrue(queryRawContactIsStarred(r3));
3241        assertTrue(queryRawContactIsStarred(r4));
3242        assertFalse(queryRawContactIsStarred(r5));
3243        assertTrue(queryRawContactIsStarred(r6));
3244        assertFalse(queryRawContactIsStarred(r7));
3245
3246        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3247        Cursor c = queryGroupMemberships(mAccount);
3248        try {
3249            assertTrue(c.moveToNext());
3250            assertEquals(g1, c.getLong(0));
3251            assertEquals(r1, c.getLong(1));
3252            assertTrue(c.moveToNext());
3253            assertEquals(g1, c.getLong(0));
3254            assertEquals(r3, c.getLong(1));
3255            assertFalse(c.moveToNext());
3256        } finally {
3257            c.close();
3258        }
3259
3260        updateItem(RawContacts.CONTENT_URI, r6,
3261                RawContacts.ACCOUNT_NAME, mAccount.name,
3262                RawContacts.ACCOUNT_TYPE, mAccount.type);
3263        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3264        c = queryGroupMemberships(mAccount);
3265        try {
3266            assertTrue(c.moveToNext());
3267            assertEquals(g1, c.getLong(0));
3268            assertEquals(r1, c.getLong(1));
3269            assertTrue(c.moveToNext());
3270            assertEquals(g1, c.getLong(0));
3271            assertEquals(r3, c.getLong(1));
3272            assertTrue(c.moveToNext());
3273            assertEquals(g1, c.getLong(0));
3274            assertEquals(r6, c.getLong(1));
3275            assertFalse(c.moveToNext());
3276        } finally {
3277            c.close();
3278        }
3279
3280        mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null);
3281
3282        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3283        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3284
3285        assertTrue(queryRawContactIsStarred(r1));
3286        assertFalse(queryRawContactIsStarred(r2));
3287        assertTrue(queryRawContactIsStarred(r3));
3288        assertTrue(queryRawContactIsStarred(r4));
3289        assertFalse(queryRawContactIsStarred(r5));
3290        assertTrue(queryRawContactIsStarred(r6));
3291        assertFalse(queryRawContactIsStarred(r7));
3292    }
3293
3294    public void testFavoritesGroupMembershipChangeAfterStarChange() {
3295        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
3296        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
3297        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
3298        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
3299        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
3300        long r2 = createRawContact(mAccount);
3301        long r3 = createRawContact(mAccountTwo);
3302
3303        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3304        Cursor c = queryGroupMemberships(mAccount);
3305        try {
3306            assertTrue(c.moveToNext());
3307            assertEquals(g1, c.getLong(0));
3308            assertEquals(r1, c.getLong(1));
3309            assertFalse(c.moveToNext());
3310        } finally {
3311            c.close();
3312        }
3313
3314        // remove the star from r1
3315        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
3316
3317        // Since no raw contacts are starred, there should be no group memberships.
3318        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3319        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3320
3321        // mark r1 as starred
3322        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1"));
3323        // Now that r1 is starred it should have a membership in the one groups from mAccount
3324        // that is marked as a favorite.
3325        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
3326        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3327        c = queryGroupMemberships(mAccount);
3328        try {
3329            assertTrue(c.moveToNext());
3330            assertEquals(g1, c.getLong(0));
3331            assertEquals(r1, c.getLong(1));
3332            assertFalse(c.moveToNext());
3333        } finally {
3334            c.close();
3335        }
3336
3337        // remove the star from r1
3338        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
3339        // Since no raw contacts are starred, there should be no group memberships.
3340        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3341        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3342
3343        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1));
3344        assertNotNull(contactUri);
3345
3346        // mark r1 as starred via its contact lookup uri
3347        assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1"));
3348        // Now that r1 is starred it should have a membership in the one groups from mAccount
3349        // that is marked as a favorite.
3350        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
3351        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3352        c = queryGroupMemberships(mAccount);
3353        try {
3354            assertTrue(c.moveToNext());
3355            assertEquals(g1, c.getLong(0));
3356            assertEquals(r1, c.getLong(1));
3357            assertFalse(c.moveToNext());
3358        } finally {
3359            c.close();
3360        }
3361
3362        // remove the star from r1
3363        updateItem(contactUri, Contacts.STARRED, "0");
3364        // Since no raw contacts are starred, there should be no group memberships.
3365        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3366        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3367    }
3368
3369    public void testStarChangedAfterGroupMembershipChange() {
3370        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
3371        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
3372        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
3373        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
3374        long r1 = createRawContact(mAccount);
3375        long r2 = createRawContact(mAccount);
3376        long r3 = createRawContact(mAccountTwo);
3377
3378        assertFalse(queryRawContactIsStarred(r1));
3379        assertFalse(queryRawContactIsStarred(r2));
3380        assertFalse(queryRawContactIsStarred(r3));
3381
3382        Cursor c;
3383
3384        // add r1 to one favorites group
3385        // r1's star should automatically be set
3386        // r1 should automatically be added to the other favorites group
3387        Uri urir1g1 = insertGroupMembership(r1, g1);
3388        assertTrue(queryRawContactIsStarred(r1));
3389        assertFalse(queryRawContactIsStarred(r2));
3390        assertFalse(queryRawContactIsStarred(r3));
3391        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3392        c = queryGroupMemberships(mAccount);
3393        try {
3394            assertTrue(c.moveToNext());
3395            assertEquals(g1, c.getLong(0));
3396            assertEquals(r1, c.getLong(1));
3397            assertFalse(c.moveToNext());
3398        } finally {
3399            c.close();
3400        }
3401
3402        // remove r1 from one favorites group
3403        mResolver.delete(urir1g1, null, null);
3404        // r1's star should no longer be set
3405        assertFalse(queryRawContactIsStarred(r1));
3406        assertFalse(queryRawContactIsStarred(r2));
3407        assertFalse(queryRawContactIsStarred(r3));
3408        // there should be no membership rows
3409        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3410        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3411
3412        // add r3 to the one favorites group for that account
3413        // r3's star should automatically be set
3414        Uri urir3g4 = insertGroupMembership(r3, g4);
3415        assertFalse(queryRawContactIsStarred(r1));
3416        assertFalse(queryRawContactIsStarred(r2));
3417        assertTrue(queryRawContactIsStarred(r3));
3418        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3419        c = queryGroupMemberships(mAccountTwo);
3420        try {
3421            assertTrue(c.moveToNext());
3422            assertEquals(g4, c.getLong(0));
3423            assertEquals(r3, c.getLong(1));
3424            assertFalse(c.moveToNext());
3425        } finally {
3426            c.close();
3427        }
3428
3429        // remove r3 from the favorites group
3430        mResolver.delete(urir3g4, null, null);
3431        // r3's star should automatically be cleared
3432        assertFalse(queryRawContactIsStarred(r1));
3433        assertFalse(queryRawContactIsStarred(r2));
3434        assertFalse(queryRawContactIsStarred(r3));
3435        assertNoRowsAndClose(queryGroupMemberships(mAccount));
3436        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
3437    }
3438
3439    public void testReadOnlyRawContact() {
3440        long rawContactId = createRawContact();
3441        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
3442        storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
3443        storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
3444
3445        storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second");
3446        assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
3447
3448        Uri syncAdapterUri = rawContactUri.buildUpon()
3449                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
3450                .build();
3451        storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third");
3452        assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third");
3453    }
3454
3455    public void testReadOnlyDataRow() {
3456        long rawContactId = createRawContact();
3457        Uri emailUri = insertEmail(rawContactId, "email");
3458        Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111");
3459
3460        storeValue(emailUri, Data.IS_READ_ONLY, "1");
3461        storeValue(emailUri, Email.ADDRESS, "changed");
3462        storeValue(phoneUri, Phone.NUMBER, "555-2222");
3463        assertStoredValue(emailUri, Email.ADDRESS, "email");
3464        assertStoredValue(phoneUri, Phone.NUMBER, "555-2222");
3465
3466        Uri syncAdapterUri = emailUri.buildUpon()
3467                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
3468                .build();
3469        storeValue(syncAdapterUri, Email.ADDRESS, "changed");
3470        assertStoredValue(emailUri, Email.ADDRESS, "changed");
3471    }
3472
3473    public void testContactWithReadOnlyRawContact() {
3474        long rawContactId1 = createRawContact();
3475        Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
3476        storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first");
3477
3478        long rawContactId2 = createRawContact();
3479        Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
3480        storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
3481        storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
3482
3483        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
3484                rawContactId1, rawContactId2);
3485
3486        long contactId = queryContactId(rawContactId1);
3487
3488        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
3489        storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
3490        assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
3491        assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt");
3492        assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
3493    }
3494
3495    private Cursor queryGroupMemberships(Account account) {
3496        Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account),
3497                new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},
3498                Data.MIMETYPE + "=?", new String[]{GroupMembership.CONTENT_ITEM_TYPE},
3499                GroupMembership.GROUP_SOURCE_ID);
3500        return c;
3501    }
3502
3503    private String readToEnd(FileInputStream inputStream) {
3504        try {
3505            int ch;
3506            StringBuilder stringBuilder = new StringBuilder();
3507            while ((ch = inputStream.read()) != -1)
3508                stringBuilder.append((char)ch);
3509            return stringBuilder.toString();
3510        } catch (IOException e) {
3511            return null;
3512        }
3513    }
3514
3515    private void assertQueryParameter(String uriString, String parameter, String expectedValue) {
3516        assertEquals(expectedValue, ContactsProvider2.getQueryParameter(
3517                Uri.parse(uriString), parameter));
3518    }
3519
3520    private long createContact(ContentValues values, String firstName, String givenName,
3521            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
3522            long groupId, int chatMode) {
3523        return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email,
3524                presenceStatus, timesContacted, starred, groupId, chatMode));
3525    }
3526
3527    private long createRawContact(ContentValues values, String firstName, String givenName,
3528            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
3529            long groupId, int chatMode) {
3530        long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
3531                timesContacted, starred, groupId, chatMode);
3532        insertStructuredName(rawContactId, firstName, givenName);
3533        return rawContactId;
3534    }
3535
3536    private long createRawContact(ContentValues values, String phoneNumber, String email,
3537            int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) {
3538        values.put(RawContacts.STARRED, starred);
3539        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
3540        values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5");
3541        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
3542        values.put(RawContacts.TIMES_CONTACTED, timesContacted);
3543        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
3544        long rawContactId = ContentUris.parseId(rawContactUri);
3545        Uri photoUri = insertPhoto(rawContactId);
3546        long photoId = ContentUris.parseId(photoUri);
3547        values.put(Contacts.PHOTO_ID, photoId);
3548        insertPhoneNumber(rawContactId, phoneNumber);
3549        insertEmail(rawContactId, email);
3550
3551        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking",
3552                chatMode);
3553
3554        if (groupId != 0) {
3555            insertGroupMembership(rawContactId, groupId);
3556        }
3557        return rawContactId;
3558    }
3559
3560    private void putDataValues(ContentValues values, long rawContactId) {
3561        values.put(Data.RAW_CONTACT_ID, rawContactId);
3562        values.put(Data.MIMETYPE, "testmimetype");
3563        values.put(Data.RES_PACKAGE, "oldpackage");
3564        values.put(Data.IS_PRIMARY, 1);
3565        values.put(Data.IS_SUPER_PRIMARY, 1);
3566        values.put(Data.DATA1, "one");
3567        values.put(Data.DATA2, "two");
3568        values.put(Data.DATA3, "three");
3569        values.put(Data.DATA4, "four");
3570        values.put(Data.DATA5, "five");
3571        values.put(Data.DATA6, "six");
3572        values.put(Data.DATA7, "seven");
3573        values.put(Data.DATA8, "eight");
3574        values.put(Data.DATA9, "nine");
3575        values.put(Data.DATA10, "ten");
3576        values.put(Data.DATA11, "eleven");
3577        values.put(Data.DATA12, "twelve");
3578        values.put(Data.DATA13, "thirteen");
3579        values.put(Data.DATA14, "fourteen");
3580        values.put(Data.DATA15, "fifteen");
3581        values.put(Data.SYNC1, "sync1");
3582        values.put(Data.SYNC2, "sync2");
3583        values.put(Data.SYNC3, "sync3");
3584        values.put(Data.SYNC4, "sync4");
3585    }
3586}
3587
3588