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