ContactsProvider2Test.java revision e3e79030101447da07547647bad225686eb9b8df
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.providers.contacts;
18
19import com.android.internal.util.ArrayUtils;
20import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
21import com.google.android.collect.Lists;
22
23import android.accounts.Account;
24import android.content.ContentProviderOperation;
25import android.content.ContentProviderResult;
26import android.content.ContentUris;
27import android.content.ContentValues;
28import android.content.Entity;
29import android.content.EntityIterator;
30import android.content.res.AssetFileDescriptor;
31import android.database.Cursor;
32import android.net.Uri;
33import android.os.RemoteException;
34import android.provider.ContactsContract;
35import android.provider.LiveFolders;
36import android.provider.OpenableColumns;
37import android.provider.ContactsContract.AggregationExceptions;
38import android.provider.ContactsContract.ContactCounts;
39import android.provider.ContactsContract.Contacts;
40import android.provider.ContactsContract.Data;
41import android.provider.ContactsContract.DisplayNameSources;
42import android.provider.ContactsContract.Groups;
43import android.provider.ContactsContract.PhoneLookup;
44import android.provider.ContactsContract.PhoneticNameStyle;
45import android.provider.ContactsContract.ProviderStatus;
46import android.provider.ContactsContract.RawContacts;
47import android.provider.ContactsContract.RawContactsEntity;
48import android.provider.ContactsContract.SearchSnippetColumns;
49import android.provider.ContactsContract.Settings;
50import android.provider.ContactsContract.StatusUpdates;
51import android.provider.ContactsContract.CommonDataKinds.Email;
52import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
53import android.provider.ContactsContract.CommonDataKinds.Im;
54import android.provider.ContactsContract.CommonDataKinds.Nickname;
55import android.provider.ContactsContract.CommonDataKinds.Organization;
56import android.provider.ContactsContract.CommonDataKinds.Phone;
57import android.provider.ContactsContract.CommonDataKinds.Photo;
58import android.provider.ContactsContract.CommonDataKinds.StructuredName;
59import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
60import android.test.MoreAsserts;
61import android.test.suitebuilder.annotation.LargeTest;
62
63import java.io.FileInputStream;
64import java.io.IOException;
65import java.text.Collator;
66import java.util.Arrays;
67import java.util.Locale;
68
69
70/**
71 * Unit tests for {@link ContactsProvider2}.
72 *
73 * Run the test like this:
74 * <code>
75 * adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
76 *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
77 * </code>
78 */
79@LargeTest
80public class ContactsProvider2Test extends BaseContactsProvider2Test {
81
82    public void testRawContactsInsert() {
83        ContentValues values = new ContentValues();
84
85        values.put(RawContacts.ACCOUNT_NAME, "a");
86        values.put(RawContacts.ACCOUNT_TYPE, "b");
87        values.put(RawContacts.SOURCE_ID, "c");
88        values.put(RawContacts.VERSION, 42);
89        values.put(RawContacts.DIRTY, 1);
90        values.put(RawContacts.DELETED, 1);
91        values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
92        values.put(RawContacts.CUSTOM_RINGTONE, "d");
93        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
94        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
95        values.put(RawContacts.STARRED, 1);
96        values.put(RawContacts.SYNC1, "e");
97        values.put(RawContacts.SYNC2, "f");
98        values.put(RawContacts.SYNC3, "g");
99        values.put(RawContacts.SYNC4, "h");
100
101        Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
102        long rawContactId = ContentUris.parseId(rowUri);
103
104        assertStoredValues(rowUri, values);
105        assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId);
106        assertNetworkNotified(true);
107    }
108
109    public void testDataDirectoryWithLookupUri() {
110        ContentValues values = new ContentValues();
111
112        long rawContactId = createRawContactWithName();
113        insertPhoneNumber(rawContactId, "555-GOOG-411");
114        insertEmail(rawContactId, "google@android.com");
115
116        long contactId = queryContactId(rawContactId);
117        String lookupKey = queryLookupKey(contactId);
118
119        // Complete and valid lookup URI
120        Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
121        Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
122
123        assertDataRows(dataUri, values);
124
125        // Complete but stale lookup URI
126        lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
127        dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
128        assertDataRows(dataUri, values);
129
130        // Incomplete lookup URI (lookup key only, no contact ID)
131        dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
132                lookupKey), Contacts.Data.CONTENT_DIRECTORY);
133        assertDataRows(dataUri, values);
134    }
135
136    private void assertDataRows(Uri dataUri, ContentValues values) {
137        Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
138        assertEquals(3, cursor.getCount());
139        cursor.moveToFirst();
140        values.put(Data.DATA1, "John Doe");
141        assertCursorValues(cursor, values);
142
143        cursor.moveToNext();
144        values.put(Data.DATA1, "555-GOOG-411");
145        assertCursorValues(cursor, values);
146
147        cursor.moveToNext();
148        values.put(Data.DATA1, "google@android.com");
149        assertCursorValues(cursor, values);
150
151        cursor.close();
152    }
153
154    public void testDataInsert() {
155        long rawContactId = createRawContactWithName("John", "Doe");
156
157        ContentValues values = new ContentValues();
158        putDataValues(values, rawContactId);
159        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
160        long dataId = ContentUris.parseId(dataUri);
161
162        long contactId = queryContactId(rawContactId);
163        values.put(RawContacts.CONTACT_ID, contactId);
164        assertStoredValues(dataUri, values);
165
166        assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
167
168        // Access the same data through the directory under RawContacts
169        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
170        Uri rawContactDataUri =
171                Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
172        assertSelection(rawContactDataUri, values, Data._ID, dataId);
173
174        // Access the same data through the directory under Contacts
175        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
176        Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
177        assertSelection(contactDataUri, values, Data._ID, dataId);
178        assertNetworkNotified(true);
179    }
180
181    public void testRawContactDataQuery() {
182        Account account1 = new Account("a", "b");
183        Account account2 = new Account("c", "d");
184        long rawContactId1 = createRawContact(account1);
185        Uri dataUri1 = insertStructuredName(rawContactId1, "John", "Doe");
186        long rawContactId2 = createRawContact(account2);
187        Uri dataUri2 = insertStructuredName(rawContactId2, "Jane", "Doe");
188
189        Uri uri1 = maybeAddAccountQueryParameters(dataUri1, account1);
190        Uri uri2 = maybeAddAccountQueryParameters(dataUri2, account2);
191        assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
192        assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
193    }
194
195    public void testPhonesQuery() {
196
197        ContentValues values = new ContentValues();
198        values.put(RawContacts.CUSTOM_RINGTONE, "d");
199        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
200        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
201        values.put(RawContacts.TIMES_CONTACTED, 54321);
202        values.put(RawContacts.STARRED, 1);
203
204        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
205        long rawContactId = ContentUris.parseId(rawContactUri);
206
207        insertStructuredName(rawContactId, "Meghan", "Knox");
208        Uri uri = insertPhoneNumber(rawContactId, "18004664411");
209        long phoneId = ContentUris.parseId(uri);
210
211
212        long contactId = queryContactId(rawContactId);
213        values.clear();
214        values.put(Data._ID, phoneId);
215        values.put(Data.RAW_CONTACT_ID, rawContactId);
216        values.put(RawContacts.CONTACT_ID, contactId);
217        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
218        values.put(Phone.NUMBER, "18004664411");
219        values.put(Phone.TYPE, Phone.TYPE_HOME);
220        values.putNull(Phone.LABEL);
221        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
222        values.put(Contacts.CUSTOM_RINGTONE, "d");
223        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
224        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
225        values.put(Contacts.TIMES_CONTACTED, 54321);
226        values.put(Contacts.STARRED, 1);
227
228        assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
229        assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId);
230    }
231
232    public void testPhonesFilterQuery() {
233        long rawContactId1 = createRawContactWithName("Hot", "Tamale");
234        insertPhoneNumber(rawContactId1, "18004664411");
235        insertPhoneNumber(rawContactId1, "1-800-466-4411");
236
237        long rawContactId2 = createRawContactWithName("Hot", "Tamale");
238        insertPhoneNumber(rawContactId2, "1-800-466-4411");
239
240        Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale");
241        ContentValues values = new ContentValues();
242        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
243        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
244        values.put(Phone.NUMBER, "1-800-466-4411");
245        values.put(Phone.TYPE, Phone.TYPE_HOME);
246        values.putNull(Phone.LABEL);
247        assertStoredValuesWithProjection(filterUri1, values);
248
249        Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411");
250        assertStoredValues(filterUri2, values);
251
252        Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664");
253        assertStoredValues(filterUri3, values);
254
255        Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada");
256        assertEquals(0, getCount(filterUri4, null, null));
257
258        Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*");
259        assertEquals(0, getCount(filterUri5, null, null));
260    }
261
262    public void testPhoneLookup() {
263        ContentValues values = new ContentValues();
264        values.put(RawContacts.CUSTOM_RINGTONE, "d");
265        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
266
267        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
268        long rawContactId = ContentUris.parseId(rawContactUri);
269
270        insertStructuredName(rawContactId, "Hot", "Tamale");
271        insertPhoneNumber(rawContactId, "18004664411");
272
273        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
274
275        values.clear();
276        values.put(PhoneLookup._ID, queryContactId(rawContactId));
277        values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
278        values.put(PhoneLookup.NUMBER, "18004664411");
279        values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
280        values.putNull(PhoneLookup.LABEL);
281        values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
282        values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
283        assertStoredValues(lookupUri1, values);
284
285        // The strict comparation, adopted in Donut, does not allow the behavior like
286        // "8004664411 == 4664411", while the loose comparation, which had been used in Cupcake
287        // and reverted back into the default in Eclair, allows it. Hmm...
288        final boolean useStrictComparation =
289            mContext.getResources().getBoolean(
290                    com.android.internal.R.bool.config_use_strict_phone_number_comparation);
291        final int expectedResult = (useStrictComparation ? 0 : 1);
292
293        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
294        assertEquals(expectedResult, getCount(lookupUri2, null, null));
295    }
296
297    public void testPhoneUpdate() {
298        ContentValues values = new ContentValues();
299        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
300        long rawContactId = ContentUris.parseId(rawContactUri);
301
302        insertStructuredName(rawContactId, "Hot", "Tamale");
303        Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
304
305        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
306        assertStoredValue(lookupUri1, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
307
308        values.clear();
309        values.put(Phone.NUMBER, "18004664422");
310        mResolver.update(phoneUri, values, null, null);
311
312        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
313        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
314
315        // Setting number to null will remove the phone lookup record
316        values.clear();
317        values.putNull(Phone.NUMBER);
318        mResolver.update(phoneUri, values, null, null);
319
320        assertEquals(0, getCount(lookupUri2, null, null));
321
322        // Let's restore that phone lookup record
323        values.clear();
324        values.put(Phone.NUMBER, "18004664422");
325        mResolver.update(phoneUri, values, null, null);
326        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
327        assertNetworkNotified(true);
328    }
329
330    public void testEmailsQuery() {
331        ContentValues values = new ContentValues();
332        values.put(RawContacts.CUSTOM_RINGTONE, "d");
333        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
334        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
335        values.put(RawContacts.TIMES_CONTACTED, 54321);
336        values.put(RawContacts.STARRED, 1);
337
338        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
339        long rawContactId = ContentUris.parseId(rawContactUri);
340
341        insertStructuredName(rawContactId, "Meghan", "Knox");
342        Uri uri = insertEmail(rawContactId, "meghan@acme.com");
343        long emailId = ContentUris.parseId(uri);
344
345        long contactId = queryContactId(rawContactId);
346        values.clear();
347        values.put(Data._ID, emailId);
348        values.put(Data.RAW_CONTACT_ID, rawContactId);
349        values.put(RawContacts.CONTACT_ID, contactId);
350        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
351        values.put(Email.DATA, "meghan@acme.com");
352        values.put(Email.TYPE, Email.TYPE_HOME);
353        values.putNull(Email.LABEL);
354        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
355        values.put(Contacts.CUSTOM_RINGTONE, "d");
356        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
357        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
358        values.put(Contacts.TIMES_CONTACTED, 54321);
359        values.put(Contacts.STARRED, 1);
360
361        assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
362        assertSelection(Email.CONTENT_URI, values, Data._ID, emailId);
363    }
364
365    public void testEmailsLookupQuery() {
366        long rawContactId = createRawContactWithName("Hot", "Tamale");
367        insertEmail(rawContactId, "tamale@acme.com");
368
369        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale@acme.com");
370        ContentValues values = new ContentValues();
371        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
372        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
373        values.put(Email.DATA, "tamale@acme.com");
374        values.put(Email.TYPE, Email.TYPE_HOME);
375        values.putNull(Email.LABEL);
376        assertStoredValues(filterUri1, values);
377
378        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale@acme.com>");
379        assertStoredValues(filterUri2, values);
380
381        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada@acme.com");
382        assertEquals(0, getCount(filterUri3, null, null));
383    }
384
385    public void testEmailsFilterQuery() {
386        long rawContactId1 = createRawContactWithName("Hot", "Tamale");
387        insertEmail(rawContactId1, "tamale@acme.com");
388        insertEmail(rawContactId1, "tamale@acme.com");
389
390        long rawContactId2 = createRawContactWithName("Hot", "Tamale");
391        insertEmail(rawContactId2, "tamale@acme.com");
392
393        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
394        ContentValues values = new ContentValues();
395        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
396        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
397        values.put(Email.DATA, "tamale@acme.com");
398        values.put(Email.TYPE, Email.TYPE_HOME);
399        values.putNull(Email.LABEL);
400        assertStoredValuesWithProjection(filterUri1, values);
401
402        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
403        assertStoredValuesWithProjection(filterUri2, values);
404
405        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hottamale");
406        assertStoredValuesWithProjection(filterUri3, values);
407
408        Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
409        assertStoredValuesWithProjection(filterUri4, values);
410
411        Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
412        assertEquals(0, getCount(filterUri5, null, null));
413    }
414
415    public void testPostalsQuery() {
416        long rawContactId = createRawContactWithName("Alice", "Nextore");
417        Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
418        long dataId = ContentUris.parseId(dataUri);
419
420        long contactId = queryContactId(rawContactId);
421        ContentValues values = new ContentValues();
422        values.put(Data._ID, dataId);
423        values.put(Data.RAW_CONTACT_ID, rawContactId);
424        values.put(RawContacts.CONTACT_ID, contactId);
425        values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
426        values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
427        values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
428
429        assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
430                values);
431        assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
432    }
433
434    public void testQueryContactData() {
435        ContentValues values = new ContentValues();
436        long contactId = createContact(values, "John", "Doe",
437                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0);
438        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
439
440        assertStoredValues(contactUri, values);
441        assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
442    }
443
444    public void testQueryContactWithStatusUpdate() {
445        ContentValues values = new ContentValues();
446        long contactId = createContact(values, "John", "Doe",
447                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0);
448        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
449        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
450        assertStoredValuesWithProjection(contactUri, values);
451        assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
452    }
453
454    public void testQueryContactFilter() {
455        ContentValues values = new ContentValues();
456        long rawContactId = createRawContact(values, "18004664411",
457                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0);
458
459        ContentValues nameValues = new ContentValues();
460        nameValues.put(StructuredName.GIVEN_NAME, "Stu");
461        nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
462        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
463        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
464        Uri nameUri = insertStructuredName(rawContactId, nameValues);
465
466        long contactId = queryContactId(rawContactId);
467        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
468
469        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
470        assertStoredValuesWithProjection(filterUri1, values);
471
472        assertContactFilter(contactId, "goolash");
473        assertContactFilter(contactId, "lash");
474
475        Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goolish");
476        assertEquals(0, getCount(filterUri2, null, null));
477
478        // Phonetic name with given/family reversed should not match
479        Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "lashgoo");
480        assertEquals(0, getCount(filterUri3, null, null));
481
482        nameValues.clear();
483        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
484        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
485
486        mResolver.update(nameUri, nameValues, null, null);
487
488        assertContactFilter(contactId, "galosh");
489
490        Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goolish");
491        assertEquals(0, getCount(filterUri4, null, null));
492    }
493
494    public void testQueryContactStrequent() {
495        ContentValues values1 = new ContentValues();
496        createContact(values1, "Noah", "Tever", "18004664411",
497                "a@acme.com", StatusUpdates.OFFLINE, 0, 0, 0);
498        ContentValues values2 = new ContentValues();
499        createContact(values2, "Sam", "Times", "18004664412",
500                "b@acme.com", StatusUpdates.INVISIBLE, 3, 0, 0);
501        ContentValues values3 = new ContentValues();
502        createContact(values3, "Lotta", "Calling", "18004664413",
503                "c@acme.com", StatusUpdates.AWAY, 5, 0, 0);
504        ContentValues values4 = new ContentValues();
505        createContact(values4, "Fay", "Veritt", "18004664414",
506                "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0);
507
508        Cursor c = mResolver.query(Contacts.CONTENT_STREQUENT_URI, null, null, null,
509                Contacts._ID);
510        assertEquals(3, c.getCount());
511        c.moveToFirst();
512        assertCursorValues(c, values4);
513        c.moveToNext();
514        assertCursorValues(c, values3);
515        c.moveToNext();
516        assertCursorValues(c, values2);
517        c.close();
518
519        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay");
520        c = mResolver.query(filterUri, null, null, null, Contacts._ID);
521        assertEquals(1, c.getCount());
522        c.moveToFirst();
523        assertCursorValues(c, values4);
524        c.close();
525    }
526
527    public void testQueryContactGroup() {
528        long groupId = createGroup(null, "testGroup", "Test Group");
529
530        ContentValues values1 = new ContentValues();
531        createContact(values1, "Best", "West", "18004664411",
532                "west@acme.com", StatusUpdates.OFFLINE, 0, 0, groupId);
533
534        ContentValues values2 = new ContentValues();
535        createContact(values2, "Rest", "East", "18004664422",
536                "east@acme.com", StatusUpdates.AVAILABLE, 0, 0, 0);
537
538        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
539        Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
540        assertEquals(1, c.getCount());
541        c.moveToFirst();
542        assertCursorValues(c, values1);
543        c.close();
544
545        Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
546        c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
547                new String[] { "Best West" }, Contacts._ID);
548        assertEquals(1, c.getCount());
549        c.close();
550
551        Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
552        c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
553        assertEquals(0, c.getCount());
554        c.close();
555    }
556
557    public void testPhonesWithStatusUpdate() {
558
559        ContentValues values = new ContentValues();
560        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
561        long rawContactId = ContentUris.parseId(rawContactUri);
562        insertStructuredName(rawContactId, "John", "Doe");
563        Uri photoUri = insertPhoto(rawContactId);
564        long photoId = ContentUris.parseId(photoUri);
565        values.put(Contacts.PHOTO_ID, photoId);
566        insertPhoneNumber(rawContactId, "18004664411");
567        insertPhoneNumber(rawContactId, "18004664412");
568        insertEmail(rawContactId, "goog411@acme.com");
569        insertEmail(rawContactId, "goog412@acme.com");
570
571        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
572                StatusUpdates.INVISIBLE, "Bad");
573        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412@acme.com",
574                StatusUpdates.AVAILABLE, "Good");
575        long contactId = queryContactId(rawContactId);
576
577        Uri uri = Data.CONTENT_URI;
578
579        Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND "
580                + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER);
581        assertEquals(2, c.getCount());
582
583        c.moveToFirst();
584
585        values.clear();
586        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
587        values.put(Contacts.CONTACT_STATUS, "Bad");
588        values.put(Contacts.DISPLAY_NAME, "John Doe");
589        values.put(Phone.NUMBER, "18004664411");
590        values.putNull(Phone.LABEL);
591        values.put(RawContacts.CONTACT_ID, contactId);
592        assertCursorValues(c, values);
593
594        c.moveToNext();
595
596        values.clear();
597        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
598        values.put(Contacts.CONTACT_STATUS, "Bad");
599        values.put(Contacts.DISPLAY_NAME, "John Doe");
600        values.put(Phone.NUMBER, "18004664412");
601        values.putNull(Phone.LABEL);
602        values.put(RawContacts.CONTACT_ID, contactId);
603        assertCursorValues(c, values);
604
605        c.close();
606    }
607
608    public void testGroupQuery() {
609        Account account1 = new Account("a", "b");
610        Account account2 = new Account("c", "d");
611        long groupId1 = createGroup(account1, "e", "f");
612        long groupId2 = createGroup(account2, "g", "h");
613        Uri uri1 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1);
614        Uri uri2 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2);
615        assertEquals(1, getCount(uri1, null, null));
616        assertEquals(1, getCount(uri2, null, null));
617        assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ;
618        assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ;
619    }
620
621    public void testGroupInsert() {
622        ContentValues values = new ContentValues();
623
624        values.put(Groups.ACCOUNT_NAME, "a");
625        values.put(Groups.ACCOUNT_TYPE, "b");
626        values.put(Groups.SOURCE_ID, "c");
627        values.put(Groups.VERSION, 42);
628        values.put(Groups.GROUP_VISIBLE, 1);
629        values.put(Groups.TITLE, "d");
630        values.put(Groups.TITLE_RES, 1234);
631        values.put(Groups.NOTES, "e");
632        values.put(Groups.RES_PACKAGE, "f");
633        values.put(Groups.SYSTEM_ID, "g");
634        values.put(Groups.DELETED, 1);
635        values.put(Groups.SYNC1, "h");
636        values.put(Groups.SYNC2, "i");
637        values.put(Groups.SYNC3, "j");
638        values.put(Groups.SYNC4, "k");
639
640        Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values);
641
642        values.put(Groups.DIRTY, 1);
643        assertStoredValues(rowUri, values);
644    }
645
646    public void testSettingsQuery() {
647        Account account1 = new Account("a", "b");
648        Account account2 = new Account("c", "d");
649        createSettings(account1, "0", "0");
650        createSettings(account2, "1", "1");
651        Uri uri1 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1);
652        Uri uri2 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2);
653        assertEquals(1, getCount(uri1, null, null));
654        assertEquals(1, getCount(uri2, null, null));
655        assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ;
656        assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0") ;
657        assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ;
658        assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1") ;
659    }
660
661    public void testDisplayNameParsingWhenPartsUnspecified() {
662        long rawContactId = createRawContact();
663        ContentValues values = new ContentValues();
664        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
665        insertStructuredName(rawContactId, values);
666
667        assertStructuredName(rawContactId, "Mr", "John", "Kevin", "von Smith", "Jr.");
668    }
669
670    public void testDisplayNameParsingWhenPartsAreNull() {
671        long rawContactId = createRawContact();
672        ContentValues values = new ContentValues();
673        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
674        values.putNull(StructuredName.GIVEN_NAME);
675        values.putNull(StructuredName.FAMILY_NAME);
676        insertStructuredName(rawContactId, values);
677        assertStructuredName(rawContactId, "Mr", "John", "Kevin", "von Smith", "Jr.");
678    }
679
680    public void testDisplayNameParsingWhenPartsSpecified() {
681        long rawContactId = createRawContact();
682        ContentValues values = new ContentValues();
683        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
684        values.put(StructuredName.FAMILY_NAME, "Johnson");
685        insertStructuredName(rawContactId, values);
686
687        assertStructuredName(rawContactId, null, null, null, "Johnson", null);
688    }
689
690    public void testContactWithoutPhoneticName() {
691        final long rawContactId = createRawContact(null);
692
693        ContentValues values = new ContentValues();
694        values.put(StructuredName.PREFIX, "Mr");
695        values.put(StructuredName.GIVEN_NAME, "John");
696        values.put(StructuredName.MIDDLE_NAME, "K.");
697        values.put(StructuredName.FAMILY_NAME, "Doe");
698        values.put(StructuredName.SUFFIX, "Jr.");
699        Uri dataUri = insertStructuredName(rawContactId, values);
700
701        values.clear();
702        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
703        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "John K. Doe, Jr.");
704        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Doe, John K., Jr.");
705        values.putNull(RawContacts.PHONETIC_NAME);
706        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
707        values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
708        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
709
710        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
711        assertStoredValues(rawContactUri, values);
712
713        values.clear();
714        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
715        values.put(Contacts.DISPLAY_NAME_PRIMARY, "John K. Doe, Jr.");
716        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Doe, John K., Jr.");
717        values.putNull(Contacts.PHONETIC_NAME);
718        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
719        values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
720        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
721
722        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
723                queryContactId(rawContactId));
724        assertStoredValues(contactUri, values);
725
726        // The same values should be available through a join with Data
727        assertStoredValues(dataUri, values);
728    }
729
730    public void testContactWithChineseName() {
731
732        // Only run this test when Chinese collation is supported
733        if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
734            return;
735        }
736
737        long rawContactId = createRawContact(null);
738
739        ContentValues values = new ContentValues();
740        values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B");
741        Uri dataUri = insertStructuredName(rawContactId, values);
742
743        values.clear();
744        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
745        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
746        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
747        values.putNull(RawContacts.PHONETIC_NAME);
748        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
749        values.put(RawContacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
750        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
751
752        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
753        assertStoredValues(rawContactUri, values);
754
755        values.clear();
756        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
757        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
758        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
759        values.putNull(Contacts.PHONETIC_NAME);
760        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
761        values.put(Contacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
762        values.put(Contacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
763
764        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
765                queryContactId(rawContactId));
766        assertStoredValues(contactUri, values);
767
768        // The same values should be available through a join with Data
769        assertStoredValues(dataUri, values);
770    }
771
772    public void testContactWithJapaneseName() {
773        long rawContactId = createRawContact(null);
774
775        ContentValues values = new ContentValues();
776        values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
777        values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
778        Uri dataUri = insertStructuredName(rawContactId, values);
779
780        values.clear();
781        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
782        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
783        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
784        values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
785        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
786        values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
787        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
788
789        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
790        assertStoredValues(rawContactUri, values);
791
792        values.clear();
793        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
794        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
795        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
796        values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
797        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
798        values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
799        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
800
801        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
802                queryContactId(rawContactId));
803        assertStoredValues(contactUri, values);
804
805        // The same values should be available through a join with Data
806        assertStoredValues(dataUri, values);
807    }
808
809    public void testDisplayNameUpdate() {
810        long rawContactId1 = createRawContact();
811        insertEmail(rawContactId1, "potato@acme.com", true);
812
813        long rawContactId2 = createRawContact();
814        insertPhoneNumber(rawContactId2, "123456789", true);
815
816        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
817                rawContactId1, rawContactId2);
818
819        assertAggregated(rawContactId1, rawContactId2, "123456789");
820
821        insertStructuredName(rawContactId2, "Potato", "Head");
822
823        assertAggregated(rawContactId1, rawContactId2, "Potato Head");
824        assertNetworkNotified(true);
825    }
826
827    public void testDisplayNameFromData() {
828        long rawContactId = createRawContact();
829        long contactId = queryContactId(rawContactId);
830        ContentValues values = new ContentValues();
831
832        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
833
834        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
835        insertEmail(rawContactId, "mike@monstersinc.com");
836        assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike@monstersinc.com");
837
838        insertEmail(rawContactId, "james@monstersinc.com", true);
839        assertStoredValue(uri, Contacts.DISPLAY_NAME, "james@monstersinc.com");
840
841        insertPhoneNumber(rawContactId, "1-800-466-4411");
842        assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411");
843
844        // If there are title and company, the company is display name.
845        values.clear();
846        values.put(Organization.COMPANY, "Monsters Inc");
847        Uri organizationUri = insertOrganization(rawContactId, values);
848        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc");
849
850        // If there is nickname, that is display name.
851        insertNickname(rawContactId, "Sully");
852        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully");
853
854        // If there is structured name, that is display name.
855        values.clear();
856        values.put(StructuredName.GIVEN_NAME, "James");
857        values.put(StructuredName.MIDDLE_NAME, "P.");
858        values.put(StructuredName.FAMILY_NAME, "Sullivan");
859        insertStructuredName(rawContactId, values);
860        assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan");
861    }
862
863    public void testDisplayNameFromOrganizationWithoutPhoneticName() {
864        long rawContactId = createRawContact();
865        long contactId = queryContactId(rawContactId);
866        ContentValues values = new ContentValues();
867
868        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
869
870        // If there is title without company, the title is display name.
871        values.clear();
872        values.put(Organization.TITLE, "Protagonist");
873        Uri organizationUri = insertOrganization(rawContactId, values);
874        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist");
875
876        // If there are title and company, the company is display name.
877        values.clear();
878        values.put(Organization.COMPANY, "Monsters Inc");
879        mResolver.update(organizationUri, values, null, null);
880
881        values.clear();
882        values.put(Contacts.DISPLAY_NAME, "Monsters Inc");
883        values.putNull(Contacts.PHONETIC_NAME);
884        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
885        values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc");
886        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc");
887        assertStoredValues(uri, values);
888    }
889
890    public void testDisplayNameFromOrganizationWithJapanesePhoneticName() {
891        long rawContactId = createRawContact();
892        long contactId = queryContactId(rawContactId);
893        ContentValues values = new ContentValues();
894
895        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
896
897        // If there is title without company, the title is display name.
898        values.clear();
899        values.put(Organization.COMPANY, "DoCoMo");
900        values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
901        Uri organizationUri = insertOrganization(rawContactId, values);
902
903        values.clear();
904        values.put(Contacts.DISPLAY_NAME, "DoCoMo");
905        values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
906        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
907        values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2");
908        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2");
909        assertStoredValues(uri, values);
910    }
911
912    public void testDisplayNameFromOrganizationWithChineseName() {
913        boolean hasChineseCollator = false;
914        final Locale locale[] = Collator.getAvailableLocales();
915        for (int i = 0; i < locale.length; i++) {
916            if (locale[i].equals(Locale.CHINA)) {
917                hasChineseCollator = true;
918                break;
919            }
920        }
921
922        if (!hasChineseCollator) {
923            return;
924        }
925
926        long rawContactId = createRawContact();
927        long contactId = queryContactId(rawContactId);
928        ContentValues values = new ContentValues();
929
930        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
931
932        // If there is title without company, the title is display name.
933        values.clear();
934        values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1");
935        Uri organizationUri = insertOrganization(rawContactId, values);
936
937        values.clear();
938        values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1");
939        values.putNull(Contacts.PHONETIC_NAME);
940        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
941        values.put(Contacts.SORT_KEY_PRIMARY, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
942        values.put(Contacts.SORT_KEY_ALTERNATIVE, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
943        assertStoredValues(uri, values);
944    }
945
946    public void testLookupByOrganization() {
947        long rawContactId = createRawContact();
948        long contactId = queryContactId(rawContactId);
949        ContentValues values = new ContentValues();
950
951        values.clear();
952        values.put(Organization.COMPANY, "acmecorp");
953        values.put(Organization.TITLE, "president");
954        Uri organizationUri = insertOrganization(rawContactId, values);
955
956        assertContactFilter(contactId, "acmecorp");
957        assertContactFilter(contactId, "president");
958
959        values.clear();
960        values.put(Organization.DEPARTMENT, "software");
961        mResolver.update(organizationUri, values, null, null);
962
963        assertContactFilter(contactId, "acmecorp");
964        assertContactFilter(contactId, "president");
965
966        values.clear();
967        values.put(Organization.COMPANY, "incredibles");
968        mResolver.update(organizationUri, values, null, null);
969
970        assertContactFilter(contactId, "incredibles");
971        assertContactFilter(contactId, "president");
972
973        values.clear();
974        values.put(Organization.TITLE, "director");
975        mResolver.update(organizationUri, values, null, null);
976
977        assertContactFilter(contactId, "incredibles");
978        assertContactFilter(contactId, "director");
979
980        values.clear();
981        values.put(Organization.COMPANY, "monsters");
982        values.put(Organization.TITLE, "scarer");
983        mResolver.update(organizationUri, values, null, null);
984
985        assertContactFilter(contactId, "monsters");
986        assertContactFilter(contactId, "scarer");
987    }
988
989    private void assertContactFilter(long contactId, String filter) {
990        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
991        assertStoredValue(filterUri, Contacts._ID, contactId);
992    }
993
994    public void testSearchSnippetOrganization() throws Exception {
995        long rawContactId = createRawContactWithName();
996        long contactId = queryContactId(rawContactId);
997
998        // Some random data element
999        insertEmail(rawContactId, "inc@corp.com");
1000
1001        ContentValues values = new ContentValues();
1002        values.clear();
1003        values.put(Organization.COMPANY, "acmecorp");
1004        values.put(Organization.TITLE, "engineer");
1005        Uri organizationUri = insertOrganization(rawContactId, values);
1006
1007        // Add another matching organization
1008        values.put(Organization.COMPANY, "acmeinc");
1009        insertOrganization(rawContactId, values);
1010
1011        // Add another non-matching organization
1012        values.put(Organization.COMPANY, "corpacme");
1013        insertOrganization(rawContactId, values);
1014
1015        // And another data element
1016        insertEmail(rawContactId, "emca@corp.com", true, Email.TYPE_CUSTOM, "Custom");
1017
1018        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
1019
1020        values.clear();
1021        values.put(Contacts._ID, contactId);
1022        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(organizationUri));
1023        values.put(SearchSnippetColumns.SNIPPET_DATA1, "acmecorp");
1024        values.put(SearchSnippetColumns.SNIPPET_DATA4, "engineer");
1025        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1026        assertStoredValues(filterUri, values);
1027    }
1028
1029    public void testSearchSnippetEmail() throws Exception {
1030        long rawContactId = createRawContact();
1031        long contactId = queryContactId(rawContactId);
1032        ContentValues values = new ContentValues();
1033
1034        Uri dataUri = insertEmail(rawContactId, "acme@corp.com", true, Email.TYPE_CUSTOM, "Custom");
1035
1036        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
1037
1038        values.clear();
1039        values.put(Contacts._ID, contactId);
1040        values.put(SearchSnippetColumns.SNIPPET_DATA1, "acme@corp.com");
1041        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(dataUri));
1042        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Email.CONTENT_ITEM_TYPE);
1043        values.put(SearchSnippetColumns.SNIPPET_DATA2, Email.TYPE_CUSTOM);
1044        values.put(SearchSnippetColumns.SNIPPET_DATA3, "Custom");
1045        assertStoredValues(filterUri, values);
1046    }
1047
1048    public void testSearchSnippetNickname() throws Exception {
1049        long rawContactId = createRawContactWithName();
1050        long contactId = queryContactId(rawContactId);
1051        ContentValues values = new ContentValues();
1052
1053        Uri dataUri = insertNickname(rawContactId, "Incredible");
1054
1055        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("inc"));
1056
1057        values.clear();
1058        values.put(Contacts._ID, contactId);
1059        values.put(SearchSnippetColumns.SNIPPET_DATA1, "Incredible");
1060        values.put(SearchSnippetColumns.SNIPPET_DATA_ID, ContentUris.parseId(dataUri));
1061        values.put(SearchSnippetColumns.SNIPPET_MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
1062        assertStoredValues(filterUri, values);
1063    }
1064
1065    public void testDisplayNameUpdateFromStructuredNameUpdate() {
1066        long rawContactId = createRawContact();
1067        Uri nameUri = insertStructuredName(rawContactId, "Slinky", "Dog");
1068
1069        long contactId = queryContactId(rawContactId);
1070
1071        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1072        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog");
1073
1074        ContentValues values = new ContentValues();
1075        values.putNull(StructuredName.FAMILY_NAME);
1076
1077        mResolver.update(nameUri, values, null, null);
1078        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky");
1079
1080        values.putNull(StructuredName.GIVEN_NAME);
1081
1082        mResolver.update(nameUri, values, null, null);
1083        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
1084
1085        values.put(StructuredName.FAMILY_NAME, "Dog");
1086        mResolver.update(nameUri, values, null, null);
1087
1088        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog");
1089    }
1090
1091    public void testInsertDataWithContentProviderOperations() throws Exception {
1092        ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
1093                .withValues(new ContentValues())
1094                .build();
1095        ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI)
1096                .withValueBackReference(Data.RAW_CONTACT_ID, 0)
1097                .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
1098                .withValue(StructuredName.GIVEN_NAME, "John")
1099                .withValue(StructuredName.FAMILY_NAME, "Doe")
1100                .build();
1101        ContentProviderResult[] results =
1102                mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2));
1103        long contactId = queryContactId(ContentUris.parseId(results[0].uri));
1104        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1105        assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe");
1106    }
1107
1108    public void testSendToVoicemailDefault() {
1109        long rawContactId = createRawContactWithName();
1110        long contactId = queryContactId(rawContactId);
1111
1112        Cursor c = queryContact(contactId);
1113        assertTrue(c.moveToNext());
1114        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
1115        assertEquals(0, sendToVoicemail);
1116        c.close();
1117    }
1118
1119    public void testSetSendToVoicemailAndRingtone() {
1120        long rawContactId = createRawContactWithName();
1121        long contactId = queryContactId(rawContactId);
1122
1123        updateSendToVoicemailAndRingtone(contactId, true, "foo");
1124        assertSendToVoicemailAndRingtone(contactId, true, "foo");
1125        assertNetworkNotified(false);
1126
1127        updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
1128        assertSendToVoicemailAndRingtone(contactId, false, "bar");
1129        assertNetworkNotified(false);
1130    }
1131
1132    public void testSendToVoicemailAndRingtoneAfterAggregation() {
1133        long rawContactId1 = createRawContactWithName("a", "b");
1134        long contactId1 = queryContactId(rawContactId1);
1135        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
1136
1137        long rawContactId2 = createRawContactWithName("c", "d");
1138        long contactId2 = queryContactId(rawContactId2);
1139        updateSendToVoicemailAndRingtone(contactId2, true, "bar");
1140
1141        // Aggregate them
1142        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1143                rawContactId1, rawContactId2);
1144
1145        // Both contacts had "send to VM", the contact now has the same value
1146        assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar
1147    }
1148
1149    public void testDoNotSendToVoicemailAfterAggregation() {
1150        long rawContactId1 = createRawContactWithName("e", "f");
1151        long contactId1 = queryContactId(rawContactId1);
1152        updateSendToVoicemailAndRingtone(contactId1, true, null);
1153
1154        long rawContactId2 = createRawContactWithName("g", "h");
1155        long contactId2 = queryContactId(rawContactId2);
1156        updateSendToVoicemailAndRingtone(contactId2, false, null);
1157
1158        // Aggregate them
1159        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1160                rawContactId1, rawContactId2);
1161
1162        // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
1163        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null);
1164    }
1165
1166    public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
1167        long rawContactId1 = createRawContactWithName("i", "j");
1168        long contactId1 = queryContactId(rawContactId1);
1169        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
1170
1171        long rawContactId2 = createRawContactWithName("k", "l");
1172        long contactId2 = queryContactId(rawContactId2);
1173        updateSendToVoicemailAndRingtone(contactId2, false, "bar");
1174
1175        // Aggregate them
1176        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1177                rawContactId1, rawContactId2);
1178
1179        // Split them
1180        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
1181                rawContactId1, rawContactId2);
1182
1183        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo");
1184        assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
1185    }
1186
1187    public void testStatusUpdateInsert() {
1188        long rawContactId = createRawContact();
1189        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1190        long dataId = ContentUris.parseId(imUri);
1191
1192        ContentValues values = new ContentValues();
1193        values.put(StatusUpdates.DATA_ID, dataId);
1194        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
1195        values.putNull(StatusUpdates.CUSTOM_PROTOCOL);
1196        values.put(StatusUpdates.IM_HANDLE, "aim");
1197        values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE);
1198        values.put(StatusUpdates.STATUS, "Hiding");
1199        values.put(StatusUpdates.STATUS_TIMESTAMP, 100);
1200        values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c");
1201        values.put(StatusUpdates.STATUS_ICON, 1234);
1202        values.put(StatusUpdates.STATUS_LABEL, 2345);
1203
1204        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
1205
1206        assertStoredValues(resultUri, values);
1207
1208        long contactId = queryContactId(rawContactId);
1209        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1210
1211        values.clear();
1212        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1213        values.put(Contacts.CONTACT_STATUS, "Hiding");
1214        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
1215        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c");
1216        values.put(Contacts.CONTACT_STATUS_ICON, 1234);
1217        values.put(Contacts.CONTACT_STATUS_LABEL, 2345);
1218
1219        assertStoredValues(contactUri, values);
1220
1221        values.clear();
1222        values.put(StatusUpdates.DATA_ID, dataId);
1223        values.put(StatusUpdates.STATUS, "Cloaked");
1224        values.put(StatusUpdates.STATUS_TIMESTAMP, 200);
1225        values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f");
1226        values.put(StatusUpdates.STATUS_ICON, 4321);
1227        values.put(StatusUpdates.STATUS_LABEL, 5432);
1228        mResolver.insert(StatusUpdates.CONTENT_URI, values);
1229
1230        values.clear();
1231        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1232        values.put(Contacts.CONTACT_STATUS, "Cloaked");
1233        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200);
1234        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f");
1235        values.put(Contacts.CONTACT_STATUS_ICON, 4321);
1236        values.put(Contacts.CONTACT_STATUS_LABEL, 5432);
1237        assertStoredValues(contactUri, values);
1238    }
1239
1240    public void testStatusUpdateInferAttribution() {
1241        long rawContactId = createRawContact();
1242        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1243        long dataId = ContentUris.parseId(imUri);
1244
1245        ContentValues values = new ContentValues();
1246        values.put(StatusUpdates.DATA_ID, dataId);
1247        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
1248        values.put(StatusUpdates.IM_HANDLE, "aim");
1249        values.put(StatusUpdates.STATUS, "Hiding");
1250
1251        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
1252
1253        values.clear();
1254        values.put(StatusUpdates.DATA_ID, dataId);
1255        values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim);
1256        values.put(StatusUpdates.STATUS, "Hiding");
1257
1258        assertStoredValues(resultUri, values);
1259    }
1260
1261    public void testStatusUpdateMatchingImOrEmail() {
1262        long rawContactId = createRawContact();
1263        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1264        insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im");
1265        insertEmail(rawContactId, "m@acme.com");
1266
1267        // Match on IM (standard)
1268        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available");
1269
1270        // Match on IM (custom)
1271        insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle");
1272
1273        // Match on Email
1274        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m@acme.com", StatusUpdates.AWAY, "Away");
1275
1276        // No match
1277        insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away");
1278
1279        Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] {
1280                StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL,
1281                StatusUpdates.PRESENCE, StatusUpdates.STATUS},
1282                PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID);
1283        assertTrue(c.moveToNext());
1284        assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available");
1285        assertTrue(c.moveToNext());
1286        assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle");
1287        assertTrue(c.moveToNext());
1288        assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away");
1289        assertFalse(c.moveToNext());
1290        c.close();
1291
1292        long contactId = queryContactId(rawContactId);
1293        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1294
1295        ContentValues values = new ContentValues();
1296        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
1297        values.put(Contacts.CONTACT_STATUS, "Available");
1298        assertStoredValuesWithProjection(contactUri, values);
1299    }
1300
1301    public void testStatusUpdateUpdateAndDelete() {
1302        long rawContactId = createRawContact();
1303        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1304
1305        long contactId = queryContactId(rawContactId);
1306        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1307
1308        ContentValues values = new ContentValues();
1309        values.putNull(Contacts.CONTACT_PRESENCE);
1310        values.putNull(Contacts.CONTACT_STATUS);
1311        assertStoredValuesWithProjection(contactUri, values);
1312
1313        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY");
1314        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY");
1315        Uri statusUri =
1316            insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available");
1317        long statusId = ContentUris.parseId(statusUri);
1318
1319        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
1320        values.put(Contacts.CONTACT_STATUS, "Available");
1321        assertStoredValuesWithProjection(contactUri, values);
1322
1323        // update status_updates table to set new values for
1324        //     status_updates.status
1325        //     status_updates.status_ts
1326        //     presence
1327        long updatedTs = 200;
1328        String testUpdate = "test_update";
1329        String selection = StatusUpdates.DATA_ID + "=" + statusId;
1330        values.clear();
1331        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
1332        values.put(StatusUpdates.STATUS, testUpdate);
1333        values.put(StatusUpdates.PRESENCE, "presence_test");
1334        mResolver.update(StatusUpdates.CONTENT_URI, values,
1335                StatusUpdates.DATA_ID + "=" + statusId, null);
1336        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
1337
1338        // update status_updates table to set new values for columns in status_updates table ONLY
1339        // i.e., no rows in presence table are to be updated.
1340        updatedTs = 300;
1341        testUpdate = "test_update_new";
1342        selection = StatusUpdates.DATA_ID + "=" + statusId;
1343        values.clear();
1344        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
1345        values.put(StatusUpdates.STATUS, testUpdate);
1346        mResolver.update(StatusUpdates.CONTENT_URI, values,
1347                StatusUpdates.DATA_ID + "=" + statusId, null);
1348        // make sure the presence column value is still the old value
1349        values.put(StatusUpdates.PRESENCE, "presence_test");
1350        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
1351
1352        // update status_updates table to set new values for columns in presence table ONLY
1353        // i.e., no rows in status_updates table are to be updated.
1354        selection = StatusUpdates.DATA_ID + "=" + statusId;
1355        values.clear();
1356        values.put(StatusUpdates.PRESENCE, "presence_test_new");
1357        mResolver.update(StatusUpdates.CONTENT_URI, values,
1358                StatusUpdates.DATA_ID + "=" + statusId, null);
1359        // make sure the status_updates table is not updated
1360        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
1361        values.put(StatusUpdates.STATUS, testUpdate);
1362        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
1363
1364        // effect "delete status_updates" operation and expect the following
1365        //   data deleted from status_updates table
1366        //   presence set to null
1367        mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null);
1368        values.clear();
1369        values.putNull(Contacts.CONTACT_PRESENCE);
1370        assertStoredValuesWithProjection(contactUri, values);
1371    }
1372
1373    public void testStatusUpdateWithTimestamp() {
1374        long rawContactId = createRawContact();
1375        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
1376        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
1377
1378        long contactId = queryContactId(rawContactId);
1379        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1380        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80);
1381        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100);
1382        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90);
1383
1384        // Should return the latest status
1385        ContentValues values = new ContentValues();
1386        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
1387        values.put(Contacts.CONTACT_STATUS, "Available");
1388        assertStoredValuesWithProjection(contactUri, values);
1389    }
1390
1391    private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence,
1392            String status) {
1393        ContentValues values = new ContentValues();
1394        values.put(StatusUpdates.PROTOCOL, protocol);
1395        values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
1396        values.put(StatusUpdates.PRESENCE_STATUS, presence);
1397        values.put(StatusUpdates.STATUS, status);
1398        assertCursorValues(c, values);
1399    }
1400
1401    public void testSingleStatusUpdateRowPerContact() {
1402        int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
1403        String handle1 = "test@gmail.com";
1404
1405        long rawContactId1 = createRawContact();
1406        insertImHandle(rawContactId1, protocol1, null, handle1);
1407
1408        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green");
1409        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow");
1410        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red");
1411
1412        Cursor c = queryContact(queryContactId(rawContactId1),
1413                new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS});
1414        assertEquals(1, c.getCount());
1415
1416        c.moveToFirst();
1417        assertEquals(StatusUpdates.INVISIBLE, c.getInt(0));
1418        assertEquals("Red", c.getString(1));
1419        c.close();
1420    }
1421
1422    private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail,
1423            String ringtone) {
1424        ContentValues values = new ContentValues();
1425        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
1426        if (ringtone != null) {
1427            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
1428        }
1429
1430        final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1431        int count = mResolver.update(uri, values, null, null);
1432        assertEquals(1, count);
1433    }
1434
1435    private void updateSendToVoicemailAndRingtoneWithSelection(long contactId,
1436            boolean sendToVoicemail, String ringtone) {
1437        ContentValues values = new ContentValues();
1438        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
1439        if (ringtone != null) {
1440            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
1441        }
1442
1443        int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId,
1444                null);
1445        assertEquals(1, count);
1446    }
1447
1448    private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail,
1449            String expectedRingtone) {
1450        Cursor c = queryContact(contactId);
1451        assertTrue(c.moveToNext());
1452        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
1453        assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
1454        String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE));
1455        if (expectedRingtone == null) {
1456            assertNull(ringtone);
1457        } else {
1458            assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
1459        }
1460        c.close();
1461    }
1462
1463    public void testGroupCreationAfterMembershipInsert() {
1464        long rawContactId1 = createRawContact(mAccount);
1465        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
1466
1467        long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
1468        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
1469                rawContactId1, groupId, "gsid1");
1470    }
1471
1472    public void testGroupReuseAfterMembershipInsert() {
1473        long rawContactId1 = createRawContact(mAccount);
1474        long groupId1 = createGroup(mAccount, "gsid1", "title1");
1475        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
1476
1477        assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
1478        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
1479                rawContactId1, groupId1, "gsid1");
1480    }
1481
1482    public void testGroupInsertFailureOnGroupIdConflict() {
1483        long rawContactId1 = createRawContact(mAccount);
1484        long groupId1 = createGroup(mAccount, "gsid1", "title1");
1485
1486        ContentValues values = new ContentValues();
1487        values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1);
1488        values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
1489        values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
1490        values.put(GroupMembership.GROUP_ROW_ID, groupId1);
1491        try {
1492            mResolver.insert(Data.CONTENT_URI, values);
1493            fail("the insert was expected to fail, but it succeeded");
1494        } catch (IllegalArgumentException e) {
1495            // this was expected
1496        }
1497    }
1498
1499    public void testContactVisibilityUpdateOnMembershipChange() {
1500        long rawContactId = createRawContact(mAccount);
1501        assertVisibility(rawContactId, "0");
1502
1503        long visibleGroupId = createGroup(mAccount, "123", "Visible", 1);
1504        long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0);
1505
1506        Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId);
1507        assertVisibility(rawContactId, "1");
1508
1509        Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId);
1510        assertVisibility(rawContactId, "1");
1511
1512        mResolver.delete(membership1, null, null);
1513        assertVisibility(rawContactId, "0");
1514
1515        ContentValues values = new ContentValues();
1516        values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId);
1517
1518        mResolver.update(membership2, values, null, null);
1519        assertVisibility(rawContactId, "1");
1520    }
1521
1522    private void assertVisibility(long rawContactId, String expectedValue) {
1523        assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId),
1524                null, Contacts.IN_VISIBLE_GROUP, expectedValue);
1525    }
1526
1527    public void testContentEntityIterator() throws RemoteException {
1528        // create multiple contacts and check that the selected ones are returned
1529        long id;
1530
1531        long groupId1 = createGroup(mAccount, "gsid1", "title1");
1532        long groupId2 = createGroup(mAccount, "gsid2", "title2");
1533
1534        id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c0");
1535        insertGroupMembership(id, "gsid1");
1536        insertEmail(id, "c0@email.com");
1537        insertPhoneNumber(id, "5551212c0");
1538
1539        long c1 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c1");
1540        Uri id_1_0 = insertGroupMembership(id, "gsid1");
1541        Uri id_1_1 = insertGroupMembership(id, "gsid2");
1542        Uri id_1_2 = insertEmail(id, "c1@email.com");
1543        Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
1544
1545        long c2 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c2");
1546        Uri id_2_0 = insertGroupMembership(id, "gsid1");
1547        Uri id_2_1 = insertEmail(id, "c2@email.com");
1548        Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
1549
1550        long c3 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c3");
1551        Uri id_3_0 = insertGroupMembership(id, groupId2);
1552        Uri id_3_1 = insertEmail(id, "c3@email.com");
1553        Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
1554
1555        EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query(
1556                maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), null,
1557                RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null));
1558        Entity entity;
1559        ContentValues[] subValues;
1560        entity = iterator.next();
1561        assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
1562        subValues = asSortedContentValuesArray(entity.getSubValues());
1563        assertEquals(4, subValues.length);
1564        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
1565                Data._ID, id_1_0,
1566                GroupMembership.GROUP_ROW_ID, groupId1,
1567                GroupMembership.GROUP_SOURCE_ID, "gsid1");
1568        assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
1569                Data._ID, id_1_1,
1570                GroupMembership.GROUP_ROW_ID, groupId2,
1571                GroupMembership.GROUP_SOURCE_ID, "gsid2");
1572        assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
1573                Data._ID, id_1_2,
1574                Email.DATA, "c1@email.com");
1575        assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
1576                Data._ID, id_1_3,
1577                Email.DATA, "5551212c1");
1578
1579        entity = iterator.next();
1580        assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
1581        subValues = asSortedContentValuesArray(entity.getSubValues());
1582        assertEquals(3, subValues.length);
1583        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
1584                Data._ID, id_2_0,
1585                GroupMembership.GROUP_ROW_ID, groupId1,
1586                GroupMembership.GROUP_SOURCE_ID, "gsid1");
1587        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
1588                Data._ID, id_2_1,
1589                Email.DATA, "c2@email.com");
1590        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
1591                Data._ID, id_2_2,
1592                Email.DATA, "5551212c2");
1593
1594        entity = iterator.next();
1595        assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
1596        subValues = asSortedContentValuesArray(entity.getSubValues());
1597        assertEquals(3, subValues.length);
1598        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
1599                Data._ID, id_3_0,
1600                GroupMembership.GROUP_ROW_ID, groupId2,
1601                GroupMembership.GROUP_SOURCE_ID, "gsid2");
1602        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
1603                Data._ID, id_3_1,
1604                Email.DATA, "c3@email.com");
1605        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
1606                Data._ID, id_3_2,
1607                Email.DATA, "5551212c3");
1608
1609        assertFalse(iterator.hasNext());
1610        iterator.close();
1611    }
1612
1613    public void testDataCreateUpdateDeleteByMimeType() throws Exception {
1614        long rawContactId = createRawContact();
1615
1616        ContentValues values = new ContentValues();
1617        values.put(Data.RAW_CONTACT_ID, rawContactId);
1618        values.put(Data.MIMETYPE, "testmimetype");
1619        values.put(Data.RES_PACKAGE, "oldpackage");
1620        values.put(Data.IS_PRIMARY, 1);
1621        values.put(Data.IS_SUPER_PRIMARY, 1);
1622        values.put(Data.DATA1, "old1");
1623        values.put(Data.DATA2, "old2");
1624        values.put(Data.DATA3, "old3");
1625        values.put(Data.DATA4, "old4");
1626        values.put(Data.DATA5, "old5");
1627        values.put(Data.DATA6, "old6");
1628        values.put(Data.DATA7, "old7");
1629        values.put(Data.DATA8, "old8");
1630        values.put(Data.DATA9, "old9");
1631        values.put(Data.DATA10, "old10");
1632        values.put(Data.DATA11, "old11");
1633        values.put(Data.DATA12, "old12");
1634        values.put(Data.DATA13, "old13");
1635        values.put(Data.DATA14, "old14");
1636        values.put(Data.DATA15, "old15");
1637        Uri uri = mResolver.insert(Data.CONTENT_URI, values);
1638        assertStoredValues(uri, values);
1639        assertNetworkNotified(true);
1640
1641        values.clear();
1642        values.put(Data.RES_PACKAGE, "newpackage");
1643        values.put(Data.IS_PRIMARY, 0);
1644        values.put(Data.IS_SUPER_PRIMARY, 0);
1645        values.put(Data.DATA1, "new1");
1646        values.put(Data.DATA2, "new2");
1647        values.put(Data.DATA3, "new3");
1648        values.put(Data.DATA4, "new4");
1649        values.put(Data.DATA5, "new5");
1650        values.put(Data.DATA6, "new6");
1651        values.put(Data.DATA7, "new7");
1652        values.put(Data.DATA8, "new8");
1653        values.put(Data.DATA9, "new9");
1654        values.put(Data.DATA10, "new10");
1655        values.put(Data.DATA11, "new11");
1656        values.put(Data.DATA12, "new12");
1657        values.put(Data.DATA13, "new13");
1658        values.put(Data.DATA14, "new14");
1659        values.put(Data.DATA15, "new15");
1660        mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId +
1661                " AND " + Data.MIMETYPE + "='testmimetype'", null);
1662        assertNetworkNotified(true);
1663
1664        // Should not be able to change IS_PRIMARY and IS_SUPER_PRIMARY by the above update
1665        values.put(Data.IS_PRIMARY, 1);
1666        values.put(Data.IS_SUPER_PRIMARY, 1);
1667        assertStoredValues(uri, values);
1668
1669        int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
1670                + " AND " + Data.MIMETYPE + "='testmimetype'", null);
1671        assertEquals(1, count);
1672        assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
1673                        + " AND " + Data.MIMETYPE + "='testmimetype'", null));
1674        assertNetworkNotified(true);
1675    }
1676
1677    public void testRawContactQuery() {
1678        Account account1 = new Account("a", "b");
1679        Account account2 = new Account("c", "d");
1680        long rawContactId1 = createRawContact(account1);
1681        long rawContactId2 = createRawContact(account2);
1682
1683        Uri uri1 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1);
1684        Uri uri2 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2);
1685        assertEquals(1, getCount(uri1, null, null));
1686        assertEquals(1, getCount(uri2, null, null));
1687        assertStoredValue(uri1, RawContacts._ID, rawContactId1) ;
1688        assertStoredValue(uri2, RawContacts._ID, rawContactId2) ;
1689
1690        Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1);
1691        Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2);
1692        assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ;
1693        assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ;
1694    }
1695
1696    public void testRawContactDeletion() {
1697        long rawContactId = createRawContact(mAccount);
1698        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1699
1700        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
1701        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
1702                StatusUpdates.AVAILABLE, null);
1703        long contactId = queryContactId(rawContactId);
1704
1705        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
1706                null, null));
1707        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
1708                + rawContactId, null));
1709
1710        mResolver.delete(uri, null, null);
1711
1712        assertStoredValue(uri, RawContacts.DELETED, "1");
1713        assertNetworkNotified(true);
1714
1715        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
1716        mResolver.delete(permanentDeletionUri, null, null);
1717        assertEquals(0, getCount(uri, null, null));
1718        assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
1719                null, null));
1720        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
1721                + rawContactId, null));
1722        assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
1723        assertNetworkNotified(false);
1724    }
1725
1726    public void testRawContactDeletionKeepingAggregateContact() {
1727        long rawContactId1 = createRawContactWithName(mAccount);
1728        long rawContactId2 = createRawContactWithName(mAccount);
1729
1730        // Same name - should be aggregated
1731        assertAggregated(rawContactId1, rawContactId2);
1732
1733        long contactId = queryContactId(rawContactId1);
1734
1735        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
1736        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
1737        mResolver.delete(permanentDeletionUri, null, null);
1738        assertEquals(0, getCount(uri, null, null));
1739        assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
1740    }
1741
1742    public void testRawContactDeletionWithAccounts() {
1743        long rawContactId = createRawContact(mAccount);
1744        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1745
1746        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
1747        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
1748                StatusUpdates.AVAILABLE, null);
1749        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
1750                null, null));
1751        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
1752                + rawContactId, null));
1753
1754        // Do not delete if we are deleting with wrong account.
1755        Uri deleteWithWrongAccountUri =
1756            RawContacts.CONTENT_URI.buildUpon()
1757                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name)
1758                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type)
1759                .build();
1760        mResolver.delete(deleteWithWrongAccountUri, null, null);
1761
1762        assertStoredValue(uri, RawContacts.DELETED, "0");
1763
1764        // Delete if we are deleting with correct account.
1765        Uri deleteWithCorrectAccountUri =
1766            RawContacts.CONTENT_URI.buildUpon()
1767                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name)
1768                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type)
1769                .build();
1770        mResolver.delete(deleteWithCorrectAccountUri, null, null);
1771
1772        assertStoredValue(uri, RawContacts.DELETED, "1");
1773    }
1774
1775    public void testAccountsUpdated() {
1776        // This is to ensure we do not delete contacts with null, null (account name, type)
1777        // accidentally.
1778        long rawContactId3 = createRawContactWithName("James", "Sullivan");
1779        insertPhoneNumber(rawContactId3, "5234567890");
1780        Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3);
1781        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
1782
1783        ContactsProvider2 cp = (ContactsProvider2) getProvider();
1784        cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo});
1785        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
1786        assertStoredValue(rawContact3, RawContacts.ACCOUNT_NAME, null);
1787        assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, null);
1788
1789        long rawContactId1 = createRawContact(mAccount);
1790        insertEmail(rawContactId1, "account1@email.com");
1791        long rawContactId2 = createRawContact(mAccountTwo);
1792        insertEmail(rawContactId2, "account2@email.com");
1793        insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
1794        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
1795                StatusUpdates.AVAILABLE, null);
1796
1797        cp.onAccountsUpdated(new Account[]{mAccount});
1798        assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null));
1799        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
1800                + rawContactId2, null));
1801    }
1802
1803    public void testAccountDeletion() {
1804        Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE);
1805        ContactsProvider2 cp = (ContactsProvider2) getProvider();
1806        cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount});
1807
1808        long rawContactId1 = createRawContactWithName("John", "Doe", readOnlyAccount);
1809        Uri photoUri1 = insertPhoto(rawContactId1);
1810        long rawContactId2 = createRawContactWithName("john", "doe", mAccount);
1811        Uri photoUri2 = insertPhoto(rawContactId2);
1812        storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1");
1813
1814        assertAggregated(rawContactId1, rawContactId2);
1815
1816        long contactId = queryContactId(rawContactId1);
1817
1818        // The display name should come from the writable account
1819        assertStoredValue(Uri.withAppendedPath(
1820                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
1821                Contacts.Data.CONTENT_DIRECTORY),
1822                Contacts.DISPLAY_NAME, "john doe");
1823
1824        // The photo should be the one we marked as super-primary
1825        assertStoredValue(Contacts.CONTENT_URI, contactId,
1826                Contacts.PHOTO_ID, ContentUris.parseId(photoUri2));
1827
1828        // Remove the writable account
1829        cp.onAccountsUpdated(new Account[]{readOnlyAccount});
1830
1831        // The display name should come from the remaining account
1832        assertStoredValue(Uri.withAppendedPath(
1833                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
1834                Contacts.Data.CONTENT_DIRECTORY),
1835                Contacts.DISPLAY_NAME, "John Doe");
1836
1837        // The photo should be the remaining one
1838        assertStoredValue(Contacts.CONTENT_URI, contactId,
1839                Contacts.PHOTO_ID, ContentUris.parseId(photoUri1));
1840
1841    }
1842
1843    public void testContactDeletion() {
1844        long rawContactId1 = createRawContactWithName("John", "Doe");
1845        long rawContactId2 = createRawContactWithName("John", "Doe");
1846
1847        long contactId = queryContactId(rawContactId1);
1848
1849        mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null);
1850
1851        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
1852                RawContacts.DELETED, "1");
1853        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
1854                RawContacts.DELETED, "1");
1855    }
1856
1857    public void testMarkAsDirtyParameter() {
1858        long rawContactId = createRawContact(mAccount);
1859        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1860
1861        Uri uri = insertStructuredName(rawContactId, "John", "Doe");
1862        clearDirty(rawContactUri);
1863        Uri updateUri = setCallerIsSyncAdapter(uri, mAccount);
1864
1865        ContentValues values = new ContentValues();
1866        values.put(StructuredName.FAMILY_NAME, "Dough");
1867        mResolver.update(updateUri, values, null, null);
1868        assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough");
1869        assertDirty(rawContactUri, false);
1870        assertNetworkNotified(false);
1871    }
1872
1873    public void testRawContactDirtyAndVersion() {
1874        final long rawContactId = createRawContact(mAccount);
1875        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
1876        assertDirty(uri, false);
1877        long version = getVersion(uri);
1878
1879        ContentValues values = new ContentValues();
1880        values.put(ContactsContract.RawContacts.DIRTY, 0);
1881        values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1);
1882        values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
1883                RawContacts.AGGREGATION_MODE_IMMEDIATE);
1884        values.put(ContactsContract.RawContacts.STARRED, 1);
1885        assertEquals(1, mResolver.update(uri, values, null, null));
1886        assertEquals(version, getVersion(uri));
1887
1888        assertDirty(uri, false);
1889        assertNetworkNotified(false);
1890
1891        Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
1892        assertDirty(uri, true);
1893        assertNetworkNotified(true);
1894        ++version;
1895        assertEquals(version, getVersion(uri));
1896        clearDirty(uri);
1897
1898        values = new ContentValues();
1899        values.put(Email.DATA, "goo@hoo.com");
1900        mResolver.update(emailUri, values, null, null);
1901        assertDirty(uri, true);
1902        assertNetworkNotified(true);
1903        ++version;
1904        assertEquals(version, getVersion(uri));
1905        clearDirty(uri);
1906
1907        mResolver.delete(emailUri, null, null);
1908        assertDirty(uri, true);
1909        assertNetworkNotified(true);
1910        ++version;
1911        assertEquals(version, getVersion(uri));
1912    }
1913
1914    public void testRawContactClearDirty() {
1915        final long rawContactId = createRawContact(mAccount);
1916        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
1917                rawContactId);
1918        long version = getVersion(uri);
1919        insertEmail(rawContactId, "goo@woo.com");
1920        assertDirty(uri, true);
1921        version++;
1922        assertEquals(version, getVersion(uri));
1923
1924        clearDirty(uri);
1925        assertDirty(uri, false);
1926        assertEquals(version, getVersion(uri));
1927    }
1928
1929    public void testRawContactDeletionSetsDirty() {
1930        final long rawContactId = createRawContact(mAccount);
1931        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
1932                rawContactId);
1933        long version = getVersion(uri);
1934        clearDirty(uri);
1935        assertDirty(uri, false);
1936
1937        mResolver.delete(uri, null, null);
1938        assertStoredValue(uri, RawContacts.DELETED, "1");
1939        assertDirty(uri, true);
1940        assertNetworkNotified(true);
1941        version++;
1942        assertEquals(version, getVersion(uri));
1943    }
1944
1945    public void testDeleteContactWithoutName() {
1946        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
1947        long rawContactId = ContentUris.parseId(rawContactUri);
1948
1949        Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true);
1950
1951        long contactId = queryContactId(rawContactId);
1952        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1953        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
1954
1955        int numDeleted = mResolver.delete(lookupUri, null, null);
1956        assertEquals(1, numDeleted);
1957    }
1958
1959    public void testDeleteContactWithoutAnyData() {
1960        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
1961        long rawContactId = ContentUris.parseId(rawContactUri);
1962
1963        long contactId = queryContactId(rawContactId);
1964        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1965        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
1966
1967        int numDeleted = mResolver.delete(lookupUri, null, null);
1968        assertEquals(1, numDeleted);
1969    }
1970
1971    public void testDeleteContactWithEscapedUri() {
1972        ContentValues values = new ContentValues();
1973        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
1974        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1975        long rawContactId = ContentUris.parseId(rawContactUri);
1976
1977        long contactId = queryContactId(rawContactId);
1978        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1979        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
1980        assertEquals(1, mResolver.delete(lookupUri, null, null));
1981    }
1982
1983    public void testQueryContactWithEscapedUri() {
1984        ContentValues values = new ContentValues();
1985        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
1986        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1987        long rawContactId = ContentUris.parseId(rawContactUri);
1988
1989        long contactId = queryContactId(rawContactId);
1990        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1991        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
1992        Cursor c = mResolver.query(lookupUri, null, null, null, "");
1993        assertEquals(1, c.getCount());
1994        c.close();
1995    }
1996
1997    public void testGetPhotoUri() {
1998        ContentValues values = new ContentValues();
1999        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2000        long rawContactId = ContentUris.parseId(rawContactUri);
2001        insertStructuredName(rawContactId, "John", "Doe");
2002        Uri photoUri = insertPhoto(rawContactId);
2003
2004        Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
2005                queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
2006
2007        long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
2008        assertEquals(ContentUris.parseId(photoUri), twigId);
2009    }
2010
2011    public void testOpenAssertFileDescriptorForPhoto() throws Exception {
2012        long rawContactId = createRawContact();
2013        Uri photoUri = insertPhoto(rawContactId);
2014        AssetFileDescriptor fd = mResolver.openAssetFileDescriptor(photoUri, "r");
2015        assertEquals(loadTestPhoto().length, fd.getLength());
2016
2017        Uri contactPhotoUri = Uri.withAppendedPath(
2018                ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
2019                Contacts.Photo.CONTENT_DIRECTORY);
2020        fd = mResolver.openAssetFileDescriptor(contactPhotoUri, "r");
2021        assertEquals(loadTestPhoto().length, fd.getLength());
2022    }
2023
2024    public void testSuperPrimaryPhoto() {
2025        long rawContactId1 = createRawContact(new Account("a", "a"));
2026        Uri photoUri1 = insertPhoto(rawContactId1);
2027        long photoId1 = ContentUris.parseId(photoUri1);
2028
2029        long rawContactId2 = createRawContact(new Account("b", "b"));
2030        Uri photoUri2 = insertPhoto(rawContactId2);
2031        long photoId2 = ContentUris.parseId(photoUri2);
2032
2033        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2034                rawContactId1, rawContactId2);
2035
2036        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2037                queryContactId(rawContactId1));
2038        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
2039
2040        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
2041                rawContactId1, rawContactId2);
2042
2043        ContentValues values = new ContentValues();
2044        values.put(Data.IS_SUPER_PRIMARY, 1);
2045        mResolver.update(photoUri2, values, null, null);
2046
2047        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2048                rawContactId1, rawContactId2);
2049        contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2050                queryContactId(rawContactId1));
2051        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2);
2052
2053        mResolver.update(photoUri1, values, null, null);
2054        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
2055    }
2056
2057    public void testUpdatePhoto() {
2058        ContentValues values = new ContentValues();
2059        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2060        long rawContactId = ContentUris.parseId(rawContactUri);
2061        insertStructuredName(rawContactId, "John", "Doe");
2062
2063        Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
2064                queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
2065
2066        values.clear();
2067        values.put(Data.RAW_CONTACT_ID, rawContactId);
2068        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2069        values.putNull(Photo.PHOTO);
2070        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
2071        long photoId = ContentUris.parseId(dataUri);
2072
2073        assertNull(getStoredValue(twigUri, Data._ID));
2074
2075        values.clear();
2076        values.put(Photo.PHOTO, loadTestPhoto());
2077        mResolver.update(dataUri, values, null, null);
2078        assertNetworkNotified(true);
2079
2080        long twigId = Long.parseLong(getStoredValue(twigUri, Data._ID));
2081        assertEquals(photoId, twigId);
2082    }
2083
2084    public void testUpdateRawContactDataPhoto() {
2085        // setup a contact with a null photo
2086        ContentValues values = new ContentValues();
2087        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2088        long rawContactId = ContentUris.parseId(rawContactUri);
2089
2090        // setup a photo
2091        values.put(Data.RAW_CONTACT_ID, rawContactId);
2092        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2093        values.putNull(Photo.PHOTO);
2094
2095        // try to do an update before insert should return count == 0
2096        Uri dataUri = Uri.withAppendedPath(
2097                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
2098                RawContacts.Data.CONTENT_DIRECTORY);
2099        assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
2100                new String[] {Photo.CONTENT_ITEM_TYPE}));
2101
2102        mResolver.insert(Data.CONTENT_URI, values);
2103
2104        // save a photo to the db
2105        values.clear();
2106        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
2107        values.put(Photo.PHOTO, loadTestPhoto());
2108        assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
2109                new String[] {Photo.CONTENT_ITEM_TYPE}));
2110
2111        // verify the photo
2112        Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
2113                Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
2114        storedPhoto.moveToFirst();
2115        MoreAsserts.assertEquals(loadTestPhoto(), storedPhoto.getBlob(0));
2116        storedPhoto.close();
2117    }
2118
2119    public void testUpdateRawContactSetStarred() {
2120        long rawContactId1 = createRawContactWithName();
2121        Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
2122        long rawContactId2 = createRawContactWithName();
2123        Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
2124
2125        assertAggregated(rawContactId1, rawContactId2);
2126
2127        long contactId = queryContactId(rawContactId1);
2128        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2129        assertStoredValue(contactUri, Contacts.STARRED, "0");
2130
2131        ContentValues values = new ContentValues();
2132        values.put(RawContacts.STARRED, "1");
2133
2134        mResolver.update(rawContactUri1, values, null, null);
2135
2136        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
2137        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
2138        assertStoredValue(contactUri, Contacts.STARRED, "1");
2139
2140        values.put(RawContacts.STARRED, "0");
2141        mResolver.update(rawContactUri1, values, null, null);
2142
2143        assertStoredValue(rawContactUri1, RawContacts.STARRED, "0");
2144        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
2145        assertStoredValue(contactUri, Contacts.STARRED, "0");
2146
2147        values.put(Contacts.STARRED, "1");
2148        mResolver.update(contactUri, values, null, null);
2149
2150        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
2151        assertStoredValue(rawContactUri2, RawContacts.STARRED, "1");
2152        assertStoredValue(contactUri, Contacts.STARRED, "1");
2153    }
2154
2155    public void testLiveFolders() {
2156        long rawContactId1 = createRawContactWithName("James", "Sullivan");
2157        insertPhoneNumber(rawContactId1, "5234567890");
2158        long contactId1 = queryContactId(rawContactId1);
2159
2160        long rawContactId2 = createRawContactWithName("Mike", "Wazowski");
2161        long contactId2 = queryContactId(rawContactId2);
2162        storeValue(Contacts.CONTENT_URI, contactId2, Contacts.STARRED, "1");
2163
2164        long rawContactId3 = createRawContactWithName("Randall", "Boggs");
2165        long contactId3 = queryContactId(rawContactId3);
2166        long groupId = createGroup(NO_ACCOUNT, "src1", "VIP");
2167        insertGroupMembership(rawContactId3, groupId);
2168
2169        assertLiveFolderContents(
2170                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2171                        "live_folders/contacts"),
2172                contactId1, "James Sullivan",
2173                contactId2, "Mike Wazowski",
2174                contactId3, "Randall Boggs");
2175
2176        assertLiveFolderContents(
2177                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2178                        "live_folders/contacts_with_phones"),
2179                contactId1, "James Sullivan");
2180
2181        assertLiveFolderContents(
2182                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2183                        "live_folders/favorites"),
2184                contactId2, "Mike Wazowski");
2185
2186        assertLiveFolderContents(
2187                Uri.withAppendedPath(Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
2188                        "live_folders/contacts"), Uri.encode("VIP")),
2189                contactId3, "Randall Boggs");
2190    }
2191
2192    private void assertLiveFolderContents(Uri uri, Object... expected) {
2193        Cursor c = mResolver.query(uri, new String[]{LiveFolders._ID, LiveFolders.NAME},
2194                null, null, LiveFolders._ID);
2195        assertEquals(expected.length/2, c.getCount());
2196        for (int i = 0; i < expected.length/2; i++) {
2197            assertTrue(c.moveToNext());
2198            assertEquals(((Long)expected[i * 2]).longValue(), c.getLong(0));
2199            assertEquals(expected[i * 2 + 1], c.getString(1));
2200        }
2201        c.close();
2202    }
2203
2204    public void testContactCounts() {
2205        Uri uri = Contacts.CONTENT_URI.buildUpon()
2206                .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
2207
2208        createRawContact();
2209        createRawContactWithName("James", "Sullivan");
2210        createRawContactWithName("The Abominable", "Snowman");
2211        createRawContactWithName("Mike", "Wazowski");
2212        createRawContactWithName("randall", "boggs");
2213        createRawContactWithName("Boo", null);
2214        createRawContactWithName("Mary", null);
2215        createRawContactWithName("Roz", null);
2216
2217        Cursor cursor = mResolver.query(uri,
2218                new String[]{Contacts.DISPLAY_NAME},
2219                null, null, Contacts.SORT_KEY_PRIMARY + " COLLATE LOCALIZED");
2220
2221        assertFirstLetterValues(cursor, null, "B", "J", "M", "R", "T");
2222        assertFirstLetterCounts(cursor,    1,   1,   1,   2,   2,   1);
2223        cursor.close();
2224
2225        cursor = mResolver.query(uri,
2226                new String[]{Contacts.DISPLAY_NAME},
2227                null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC");
2228
2229        assertFirstLetterValues(cursor, "W", "S", "R", "M", "B", null);
2230        assertFirstLetterCounts(cursor,   1,   2,   1,   1,   2,    1);
2231        cursor.close();
2232    }
2233
2234    private void assertFirstLetterValues(Cursor cursor, String... expected) {
2235        String[] actual = cursor.getExtras()
2236                .getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
2237        MoreAsserts.assertEquals(expected, actual);
2238    }
2239
2240    private void assertFirstLetterCounts(Cursor cursor, int... expected) {
2241        int[] actual = cursor.getExtras()
2242                .getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
2243        MoreAsserts.assertEquals(expected, actual);
2244    }
2245
2246    public void testReadBooleanQueryParameter() {
2247        assertBooleanUriParameter("foo:bar", "bool", true, true);
2248        assertBooleanUriParameter("foo:bar", "bool", false, false);
2249        assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false);
2250        assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true);
2251        assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false);
2252        assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true);
2253        assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false);
2254        assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false);
2255        assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true);
2256        assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true);
2257        assertBooleanUriParameter("foo:bar?bool", "bool", true, true);
2258    }
2259
2260    private void assertBooleanUriParameter(String uriString, String parameter,
2261            boolean defaultValue, boolean expectedValue) {
2262        assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter(
2263                Uri.parse(uriString), parameter, defaultValue));
2264    }
2265
2266    public void testGetQueryParameter() {
2267        assertQueryParameter("foo:bar", "param", null);
2268        assertQueryParameter("foo:bar?param", "param", null);
2269        assertQueryParameter("foo:bar?param=", "param", "");
2270        assertQueryParameter("foo:bar?param=val", "param", "val");
2271        assertQueryParameter("foo:bar?param=val&some=some", "param", "val");
2272        assertQueryParameter("foo:bar?some=some&param=val", "param", "val");
2273        assertQueryParameter("foo:bar?some=some&param=val&else=else", "param", "val");
2274        assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john@doe.com");
2275    }
2276
2277    public void testMissingAccountTypeParameter() {
2278        // Try querying for RawContacts only using ACCOUNT_NAME
2279        final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(
2280                RawContacts.ACCOUNT_NAME, "lolwut").build();
2281        try {
2282            final Cursor cursor = mResolver.query(queryUri, null, null, null, null);
2283            fail("Able to query with incomplete account query parameters");
2284        } catch (IllegalArgumentException e) {
2285            // Expected behavior.
2286        }
2287    }
2288
2289    public void testInsertInconsistentAccountType() {
2290        // Try inserting RawContact with inconsistent Accounts
2291        final Account red = new Account("red", "red");
2292        final Account blue = new Account("blue", "blue");
2293
2294        final ContentValues values = new ContentValues();
2295        values.put(RawContacts.ACCOUNT_NAME, red.name);
2296        values.put(RawContacts.ACCOUNT_TYPE, red.type);
2297
2298        final Uri insertUri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, blue);
2299        try {
2300            mResolver.insert(insertUri, values);
2301            fail("Able to insert RawContact with inconsistent account details");
2302        } catch (IllegalArgumentException e) {
2303            // Expected behavior.
2304        }
2305    }
2306
2307    public void testProviderStatus() throws Exception {
2308        Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI,
2309                new String[]{ProviderStatus.DATA1, ProviderStatus.STATUS}, null, null, null);
2310        assertTrue(cursor.moveToFirst());
2311        assertEquals(0, cursor.getLong(0));
2312        assertEquals(ProviderStatus.STATUS_NORMAL, cursor.getInt(1));
2313        cursor.close();
2314    }
2315
2316    public void testProperties() throws Exception {
2317        ContactsProvider2 provider = (ContactsProvider2)getProvider();
2318        ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper();
2319        assertNull(helper.getProperty("non-existent", null));
2320        assertEquals("default", helper.getProperty("non-existent", "default"));
2321
2322        helper.setProperty("existent1", "string1");
2323        helper.setProperty("existent2", "string2");
2324        assertEquals("string1", helper.getProperty("existent1", "default"));
2325        assertEquals("string2", helper.getProperty("existent2", "default"));
2326        helper.setProperty("existent1", null);
2327        assertEquals("default", helper.getProperty("existent1", "default"));
2328    }
2329
2330    private class VCardTestUriCreator {
2331        private String mLookup1;
2332        private String mLookup2;
2333
2334        public VCardTestUriCreator(String lookup1, String lookup2) {
2335            super();
2336            mLookup1 = lookup1;
2337            mLookup2 = lookup2;
2338        }
2339
2340        public Uri getUri1() {
2341            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1);
2342        }
2343
2344        public Uri getUri2() {
2345            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2);
2346        }
2347
2348        public Uri getCombinedUri() {
2349            return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI,
2350                    Uri.encode(mLookup1 + ":" + mLookup2));
2351        }
2352    }
2353
2354    private VCardTestUriCreator createVCardTestContacts() {
2355        final long rawContactId1 = createRawContact(mAccount, RawContacts.SOURCE_ID, "4:12");
2356        insertStructuredName(rawContactId1, "John", "Doe");
2357
2358        final long rawContactId2 = createRawContact(mAccount, RawContacts.SOURCE_ID, "3:4%121");
2359        insertStructuredName(rawContactId2, "Jane", "Doh");
2360
2361        final long contactId1 = queryContactId(rawContactId1);
2362        final long contactId2 = queryContactId(rawContactId2);
2363        final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
2364        final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
2365        final String lookup1 =
2366            Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2));
2367        final String lookup2 =
2368            Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2));
2369        return new VCardTestUriCreator(lookup1, lookup2);
2370    }
2371
2372    public void testQueryMultiVCard() {
2373        // No need to create any contacts here, because the query for multiple vcards
2374        // does not go into the database at all
2375        Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456"));
2376        Cursor cursor = mResolver.query(uri, null, null, null, null);
2377        assertEquals(1, cursor.getCount());
2378        assertTrue(cursor.moveToFirst());
2379        assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
2380        String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
2381
2382        // The resulting name contains date and time. Ensure that before and after are correct
2383        assertTrue(filename.startsWith("vcards_"));
2384        assertTrue(filename.endsWith(".vcf"));
2385        cursor.close();
2386    }
2387
2388    public void testQueryFileSingleVCard() {
2389        final VCardTestUriCreator contacts = createVCardTestContacts();
2390
2391        {
2392            Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null);
2393            assertEquals(1, cursor.getCount());
2394            assertTrue(cursor.moveToFirst());
2395            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
2396            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
2397            assertEquals("John Doe.vcf", filename);
2398            cursor.close();
2399        }
2400
2401        {
2402            Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null);
2403            assertEquals(1, cursor.getCount());
2404            assertTrue(cursor.moveToFirst());
2405            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
2406            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
2407            assertEquals("Jane Doh.vcf", filename);
2408            cursor.close();
2409        }
2410    }
2411
2412
2413    public void testOpenAssetFileMultiVCard() throws IOException {
2414        final VCardTestUriCreator contacts = createVCardTestContacts();
2415
2416        final AssetFileDescriptor descriptor =
2417            mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r");
2418        final FileInputStream inputStream = descriptor.createInputStream();
2419        String data = readToEnd(inputStream);
2420        inputStream.close();
2421        descriptor.close();
2422
2423        // Ensure that the resulting VCard has both contacts
2424        assertTrue(data.contains("N:Doe;John;;;"));
2425        assertTrue(data.contains("N:Doh;Jane;;;"));
2426    }
2427
2428    public void testOpenAssetFileSingleVCard() throws IOException {
2429        final VCardTestUriCreator contacts = createVCardTestContacts();
2430
2431        // Ensure that the right VCard is being created in each case
2432        {
2433            final AssetFileDescriptor descriptor =
2434                mResolver.openAssetFileDescriptor(contacts.getUri1(), "r");
2435            final FileInputStream inputStream = descriptor.createInputStream();
2436            final String data = readToEnd(inputStream);
2437            assertTrue(data.contains("N:Doe;John;;;"));
2438            assertFalse(data.contains("N:Doh;Jane;;;"));
2439
2440            inputStream.close();
2441            descriptor.close();
2442        }
2443
2444        {
2445            final AssetFileDescriptor descriptor =
2446                mResolver.openAssetFileDescriptor(contacts.getUri2(), "r");
2447            final FileInputStream inputStream = descriptor.createInputStream();
2448            final String data = readToEnd(inputStream);
2449            inputStream.close();
2450            descriptor.close();
2451
2452            assertFalse(data.contains("N:Doe;John;;;"));
2453            assertTrue(data.contains("N:Doh;Jane;;;"));
2454        }
2455    }
2456
2457    public void testAutoGroupMembership() {
2458        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
2459        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
2460        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */);
2461        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */);
2462        long r1 = createRawContact(mAccount);
2463        long r2 = createRawContact(mAccountTwo);
2464        long r3 = createRawContact(null);
2465
2466        Cursor c = queryGroupMemberships(mAccount);
2467        try {
2468            assertTrue(c.moveToNext());
2469            assertEquals(g1, c.getLong(0));
2470            assertEquals(r1, c.getLong(1));
2471            assertFalse(c.moveToNext());
2472        } finally {
2473            c.close();
2474        }
2475
2476        c = queryGroupMemberships(mAccountTwo);
2477        try {
2478            assertTrue(c.moveToNext());
2479            assertEquals(g3, c.getLong(0));
2480            assertEquals(r2, c.getLong(1));
2481            assertFalse(c.moveToNext());
2482        } finally {
2483            c.close();
2484        }
2485    }
2486
2487    public void testNoAutoAddMembershipAfterGroupCreation() {
2488        long r1 = createRawContact(mAccount);
2489        long r2 = createRawContact(mAccount);
2490        long r3 = createRawContact(mAccount);
2491        long r4 = createRawContact(mAccountTwo);
2492        long r5 = createRawContact(mAccountTwo);
2493        long r6 = createRawContact(null);
2494
2495        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2496        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2497
2498        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
2499        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
2500        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */);
2501
2502        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2503        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2504    }
2505
2506    // create some starred and non-starred contacts, some associated with account, some not
2507    // favorites group created
2508    // the starred contacts should be added to group
2509    // favorites group removed
2510    // no change to starred status
2511    public void testFavoritesMembershipAfterGroupCreation() {
2512        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
2513        long r2 = createRawContact(mAccount);
2514        long r3 = createRawContact(mAccount, RawContacts.STARRED, "1");
2515        long r4 = createRawContact(mAccountTwo, RawContacts.STARRED, "1");
2516        long r5 = createRawContact(mAccountTwo);
2517        long r6 = createRawContact(null, RawContacts.STARRED, "1");
2518        long r7 = createRawContact(null);
2519
2520        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2521        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2522
2523        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
2524        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
2525        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */);
2526
2527        assertTrue(queryRawContactIsStarred(r1));
2528        assertFalse(queryRawContactIsStarred(r2));
2529        assertTrue(queryRawContactIsStarred(r3));
2530        assertTrue(queryRawContactIsStarred(r4));
2531        assertFalse(queryRawContactIsStarred(r5));
2532        assertTrue(queryRawContactIsStarred(r6));
2533        assertFalse(queryRawContactIsStarred(r7));
2534
2535        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2536        Cursor c = queryGroupMemberships(mAccount);
2537        try {
2538            assertTrue(c.moveToNext());
2539            assertEquals(g1, c.getLong(0));
2540            assertEquals(r1, c.getLong(1));
2541            assertTrue(c.moveToNext());
2542            assertEquals(g1, c.getLong(0));
2543            assertEquals(r3, c.getLong(1));
2544            assertFalse(c.moveToNext());
2545        } finally {
2546            c.close();
2547        }
2548
2549        updateItem(RawContacts.CONTENT_URI, r6,
2550                RawContacts.ACCOUNT_NAME, mAccount.name,
2551                RawContacts.ACCOUNT_TYPE, mAccount.type);
2552        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2553        c = queryGroupMemberships(mAccount);
2554        try {
2555            assertTrue(c.moveToNext());
2556            assertEquals(g1, c.getLong(0));
2557            assertEquals(r1, c.getLong(1));
2558            assertTrue(c.moveToNext());
2559            assertEquals(g1, c.getLong(0));
2560            assertEquals(r3, c.getLong(1));
2561            assertTrue(c.moveToNext());
2562            assertEquals(g1, c.getLong(0));
2563            assertEquals(r6, c.getLong(1));
2564            assertFalse(c.moveToNext());
2565        } finally {
2566            c.close();
2567        }
2568
2569        mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null);
2570
2571        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2572        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2573
2574        assertTrue(queryRawContactIsStarred(r1));
2575        assertFalse(queryRawContactIsStarred(r2));
2576        assertTrue(queryRawContactIsStarred(r3));
2577        assertTrue(queryRawContactIsStarred(r4));
2578        assertFalse(queryRawContactIsStarred(r5));
2579        assertTrue(queryRawContactIsStarred(r6));
2580        assertFalse(queryRawContactIsStarred(r7));
2581    }
2582
2583    public void testFavoritesGroupMembershipChangeAfterStarChange() {
2584        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
2585        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
2586        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
2587        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
2588        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
2589        long r2 = createRawContact(mAccount);
2590        long r3 = createRawContact(mAccountTwo);
2591
2592        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2593        Cursor c = queryGroupMemberships(mAccount);
2594        try {
2595            assertTrue(c.moveToNext());
2596            assertEquals(g1, c.getLong(0));
2597            assertEquals(r1, c.getLong(1));
2598            assertFalse(c.moveToNext());
2599        } finally {
2600            c.close();
2601        }
2602
2603        // remove the star from r1
2604        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
2605
2606        // Since no raw contacts are starred, there should be no group memberships.
2607        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2608        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2609
2610        // mark r1 as starred
2611        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1"));
2612        // Now that r1 is starred it should have a membership in the one groups from mAccount
2613        // that is marked as a favorite.
2614        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
2615        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2616        c = queryGroupMemberships(mAccount);
2617        try {
2618            assertTrue(c.moveToNext());
2619            assertEquals(g1, c.getLong(0));
2620            assertEquals(r1, c.getLong(1));
2621            assertFalse(c.moveToNext());
2622        } finally {
2623            c.close();
2624        }
2625
2626        // remove the star from r1
2627        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
2628        // Since no raw contacts are starred, there should be no group memberships.
2629        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2630        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2631
2632        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1));
2633        assertNotNull(contactUri);
2634
2635        // mark r1 as starred via its contact lookup uri
2636        assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1"));
2637        // Now that r1 is starred it should have a membership in the one groups from mAccount
2638        // that is marked as a favorite.
2639        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
2640        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2641        c = queryGroupMemberships(mAccount);
2642        try {
2643            assertTrue(c.moveToNext());
2644            assertEquals(g1, c.getLong(0));
2645            assertEquals(r1, c.getLong(1));
2646            assertFalse(c.moveToNext());
2647        } finally {
2648            c.close();
2649        }
2650
2651        // remove the star from r1
2652        updateItem(contactUri, Contacts.STARRED, "0");
2653        // Since no raw contacts are starred, there should be no group memberships.
2654        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2655        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2656    }
2657
2658    public void testStarChangedAfterGroupMembershipChange() {
2659        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
2660        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
2661        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
2662        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
2663        long r1 = createRawContact(mAccount);
2664        long r2 = createRawContact(mAccount);
2665        long r3 = createRawContact(mAccountTwo);
2666
2667        assertFalse(queryRawContactIsStarred(r1));
2668        assertFalse(queryRawContactIsStarred(r2));
2669        assertFalse(queryRawContactIsStarred(r3));
2670
2671        Cursor c;
2672
2673        // add r1 to one favorites group
2674        // r1's star should automatically be set
2675        // r1 should automatically be added to the other favorites group
2676        Uri urir1g1 = insertGroupMembership(r1, g1);
2677        assertTrue(queryRawContactIsStarred(r1));
2678        assertFalse(queryRawContactIsStarred(r2));
2679        assertFalse(queryRawContactIsStarred(r3));
2680        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2681        c = queryGroupMemberships(mAccount);
2682        try {
2683            assertTrue(c.moveToNext());
2684            assertEquals(g1, c.getLong(0));
2685            assertEquals(r1, c.getLong(1));
2686            assertFalse(c.moveToNext());
2687        } finally {
2688            c.close();
2689        }
2690
2691        // remove r1 from one favorites group
2692        mResolver.delete(urir1g1, null, null);
2693        // r1's star should no longer be set
2694        assertFalse(queryRawContactIsStarred(r1));
2695        assertFalse(queryRawContactIsStarred(r2));
2696        assertFalse(queryRawContactIsStarred(r3));
2697        // there should be no membership rows
2698        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2699        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2700
2701        // add r3 to the one favorites group for that account
2702        // r3's star should automatically be set
2703        Uri urir3g4 = insertGroupMembership(r3, g4);
2704        assertFalse(queryRawContactIsStarred(r1));
2705        assertFalse(queryRawContactIsStarred(r2));
2706        assertTrue(queryRawContactIsStarred(r3));
2707        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2708        c = queryGroupMemberships(mAccountTwo);
2709        try {
2710            assertTrue(c.moveToNext());
2711            assertEquals(g4, c.getLong(0));
2712            assertEquals(r3, c.getLong(1));
2713            assertFalse(c.moveToNext());
2714        } finally {
2715            c.close();
2716        }
2717
2718        // remove r3 from the favorites group
2719        mResolver.delete(urir3g4, null, null);
2720        // r3's star should automatically be cleared
2721        assertFalse(queryRawContactIsStarred(r1));
2722        assertFalse(queryRawContactIsStarred(r2));
2723        assertFalse(queryRawContactIsStarred(r3));
2724        assertNoRowsAndClose(queryGroupMemberships(mAccount));
2725        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
2726    }
2727
2728    private Cursor queryGroupMemberships(Account account) {
2729        Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account),
2730                new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},
2731                Data.MIMETYPE + "=?", new String[]{GroupMembership.CONTENT_ITEM_TYPE},
2732                GroupMembership.GROUP_SOURCE_ID);
2733        return c;
2734    }
2735
2736    private String readToEnd(FileInputStream inputStream) {
2737        try {
2738            int ch;
2739            StringBuilder stringBuilder = new StringBuilder();
2740            while ((ch = inputStream.read()) != -1)
2741                stringBuilder.append((char)ch);
2742            return stringBuilder.toString();
2743        } catch (IOException e) {
2744            return null;
2745        }
2746    }
2747
2748    private void assertQueryParameter(String uriString, String parameter, String expectedValue) {
2749        assertEquals(expectedValue, ContactsProvider2.getQueryParameter(
2750                Uri.parse(uriString), parameter));
2751    }
2752
2753    private long createContact(ContentValues values, String firstName, String givenName,
2754            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
2755            long groupId) {
2756        return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email,
2757                presenceStatus, timesContacted, starred, groupId));
2758    }
2759
2760    private long createRawContact(ContentValues values, String firstName, String givenName,
2761            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
2762            long groupId) {
2763        long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
2764                timesContacted, starred, groupId);
2765        insertStructuredName(rawContactId, firstName, givenName);
2766        return rawContactId;
2767    }
2768
2769    private long createRawContact(ContentValues values, String phoneNumber, String email,
2770            int presenceStatus, int timesContacted, int starred, long groupId) {
2771        values.put(RawContacts.STARRED, starred);
2772        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2773        values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5");
2774        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
2775        values.put(RawContacts.TIMES_CONTACTED, timesContacted);
2776        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2777        long rawContactId = ContentUris.parseId(rawContactUri);
2778        Uri photoUri = insertPhoto(rawContactId);
2779        long photoId = ContentUris.parseId(photoUri);
2780        values.put(Contacts.PHOTO_ID, photoId);
2781        insertPhoneNumber(rawContactId, phoneNumber);
2782        insertEmail(rawContactId, email);
2783
2784        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking");
2785
2786        if (groupId != 0) {
2787            insertGroupMembership(rawContactId, groupId);
2788        }
2789        return rawContactId;
2790    }
2791
2792    private void putDataValues(ContentValues values, long rawContactId) {
2793        values.put(Data.RAW_CONTACT_ID, rawContactId);
2794        values.put(Data.MIMETYPE, "testmimetype");
2795        values.put(Data.RES_PACKAGE, "oldpackage");
2796        values.put(Data.IS_PRIMARY, 1);
2797        values.put(Data.IS_SUPER_PRIMARY, 1);
2798        values.put(Data.DATA1, "one");
2799        values.put(Data.DATA2, "two");
2800        values.put(Data.DATA3, "three");
2801        values.put(Data.DATA4, "four");
2802        values.put(Data.DATA5, "five");
2803        values.put(Data.DATA6, "six");
2804        values.put(Data.DATA7, "seven");
2805        values.put(Data.DATA8, "eight");
2806        values.put(Data.DATA9, "nine");
2807        values.put(Data.DATA10, "ten");
2808        values.put(Data.DATA11, "eleven");
2809        values.put(Data.DATA12, "twelve");
2810        values.put(Data.DATA13, "thirteen");
2811        values.put(Data.DATA14, "fourteen");
2812        values.put(Data.DATA15, "fifteen");
2813        values.put(Data.SYNC1, "sync1");
2814        values.put(Data.SYNC2, "sync2");
2815        values.put(Data.SYNC3, "sync3");
2816        values.put(Data.SYNC4, "sync4");
2817    }
2818}
2819
2820