LegacyContactsProviderTest.java revision fb362d1a5df250a49fad06db323b0d41fe0e3757
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 android.app.SearchManager;
20import android.content.ContentProvider;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.database.Cursor;
24import android.net.Uri;
25import android.provider.Contacts;
26import android.provider.Contacts.ContactMethods;
27import android.provider.Contacts.Extensions;
28import android.provider.Contacts.GroupMembership;
29import android.provider.Contacts.Groups;
30import android.provider.Contacts.Intents;
31import android.provider.Contacts.Organizations;
32import android.provider.Contacts.People;
33import android.provider.Contacts.Phones;
34import android.provider.Contacts.Photos;
35import android.provider.Contacts.Presence;
36import android.test.suitebuilder.annotation.LargeTest;
37
38import java.io.IOException;
39
40/**
41 * Tests for legacy contacts APIs.
42 */
43@LargeTest
44public class LegacyContactsProviderTest extends BaseContactsProvider2Test {
45
46    private static final boolean USE_LEGACY_PROVIDER = false;
47
48    @Override
49    protected Class<? extends ContentProvider> getProviderClass() {
50       return USE_LEGACY_PROVIDER ? ContactsProvider.class : SynchronousContactsProvider2.class;
51    }
52
53    @Override
54    protected String getAuthority() {
55        return Contacts.AUTHORITY;
56    }
57
58    public void testPeopleInsert() {
59        ContentValues values = new ContentValues();
60        putContactValues(values);
61
62        Uri uri = mResolver.insert(People.CONTENT_URI, values);
63        assertStoredValues(uri, values);
64        assertSelection(People.CONTENT_URI, values, People._ID, ContentUris.parseId(uri));
65    }
66
67    public void testPeopleDelete() {
68        ContentValues values = new ContentValues();
69        values.put(People.NAME, "John Doe");
70        Uri personId = mResolver.insert(People.CONTENT_URI, values);
71        mResolver.delete(personId, null, null);
72
73        Cursor c = mResolver.query(personId, null, People.NAME + "='John Doe'" , null, null);
74        assertEquals("Record count after deletion", 0, c.getCount());
75        c.close();
76
77        try {
78            mResolver.query(People.DELETED_CONTENT_URI, null, null, null, null);
79        } catch (UnsupportedOperationException e) {
80            // Expected exception
81        }
82    }
83
84    public void testPeopleFilter() {
85        ContentValues values = new ContentValues();
86        values.put(People.NAME, "Deer Doe");
87        mResolver.insert(People.CONTENT_URI, values);
88
89        values.clear();
90        values.put(People.NAME, "Dear Dough");
91        mResolver.insert(People.CONTENT_URI, values);
92
93        values.clear();
94        values.put(People.NAME, "D.R. Dauwe");
95        mResolver.insert(People.CONTENT_URI, values);
96
97        assertFilteredContacts("d", "Deer Doe", "Dear Dough", "D.R. Dauwe");
98        assertFilteredContacts("de", "Deer Doe", "Dear Dough");
99        assertFilteredContacts("dee", "Deer Doe");
100        assertFilteredContacts("der");
101    }
102
103    public void testDefaultDisplayName() {
104        ContentValues values = new ContentValues();
105        values.put(People.NAME, "John Doe");
106        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
107        assertStoredValues(personUri, People.DISPLAY_NAME, "John Doe");
108    }
109
110    public void testPrimaryOrganization() {
111        ContentValues values = new ContentValues();
112        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
113        long personId = ContentUris.parseId(personUri);
114
115        // Primary
116        values.clear();
117        values.put(Organizations.ISPRIMARY, 1);
118        values.put(Organizations.COMPANY, "Google");
119        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
120        values.put(Organizations.PERSON_ID, personId);
121        Uri orgUri1 = mResolver.insert(Organizations.CONTENT_URI, values);
122
123        // Non-primary
124        values.clear();
125        values.put(Organizations.COMPANY, "Acme");
126        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
127        values.put(Organizations.PERSON_ID, personId);
128        Uri orgUri2 = mResolver.insert(Organizations.CONTENT_URI, values);
129
130        values.clear();
131        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri1));
132        values.put(People.DISPLAY_NAME, "Google");
133        assertStoredValues(personUri, values);
134
135        // Remove the original primary organization
136        mResolver.delete(orgUri1, null, null);
137
138        values.clear();
139        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri2));
140        values.put(People.DISPLAY_NAME, "Acme");
141        assertStoredValues(personUri, values);
142
143        // Remove the remaining organization
144        mResolver.delete(orgUri2, null, null);
145
146        values.clear();
147        values.putNull(People.PRIMARY_ORGANIZATION_ID);
148        values.putNull(People.DISPLAY_NAME);
149        assertStoredValues(personUri, values);
150    }
151
152    public void testPrimaryPhone() {
153        ContentValues values = new ContentValues();
154        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
155        long personId = ContentUris.parseId(personUri);
156
157        // Primary
158        values.clear();
159        values.put(Phones.ISPRIMARY, 1);
160        values.put(Phones.TYPE, Phones.TYPE_WORK);
161        values.put(Phones.PERSON_ID, personId);
162        values.put(Phones.NUMBER, "12345");
163        Uri phoneUri1 = mResolver.insert(Phones.CONTENT_URI, values);
164
165        // Non-primary
166        values.clear();
167        values.put(Phones.TYPE, Phones.TYPE_WORK);
168        values.put(Phones.PERSON_ID, personId);
169        values.put(Phones.NUMBER, "67890");
170        Uri phoneUri2 = mResolver.insert(Phones.CONTENT_URI, values);
171
172        values.clear();
173        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri1));
174        values.put(People.DISPLAY_NAME, "12345");
175        assertStoredValues(personUri, values);
176
177        // Remove the primary phone number
178        mResolver.delete(phoneUri1, null, null);
179
180        values.clear();
181        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri2));
182        values.put(People.DISPLAY_NAME, "67890");
183        assertStoredValues(personUri, values);
184
185        // Remove the remaining phone number
186        mResolver.delete(phoneUri2, null, null);
187
188        values.clear();
189        values.putNull(People.PRIMARY_PHONE_ID);
190        values.putNull(People.DISPLAY_NAME);
191        assertStoredValues(personUri, values);
192    }
193
194    public void testPrimaryEmail() {
195        ContentValues values = new ContentValues();
196        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
197        long personId = ContentUris.parseId(personUri);
198
199        // Primary
200        values.clear();
201        values.put(ContactMethods.PERSON_ID, personId);
202        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
203        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
204        values.put(ContactMethods.DATA, "foo@acme.com");
205        values.put(ContactMethods.ISPRIMARY, 1);
206        Uri emailUri1 = mResolver.insert(ContactMethods.CONTENT_URI, values);
207
208        // Non-primary
209        values.clear();
210        values.put(ContactMethods.PERSON_ID, personId);
211        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
212        values.put(ContactMethods.TYPE, ContactMethods.TYPE_WORK);
213        values.put(ContactMethods.DATA, "bar@acme.com");
214        Uri emailUri2 = mResolver.insert(ContactMethods.CONTENT_URI, values);
215
216        values.clear();
217        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri1));
218        values.put(People.DISPLAY_NAME, "foo@acme.com");
219        assertStoredValues(personUri, values);
220
221        // Remove the primary email
222        mResolver.delete(emailUri1, null, null);
223
224        values.clear();
225        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri2));
226        values.put(People.DISPLAY_NAME, "bar@acme.com");
227        assertStoredValues(personUri, values);
228
229        // Remove the remaining email
230        mResolver.delete(emailUri2, null, null);
231
232        values.clear();
233        values.putNull(People.PRIMARY_EMAIL_ID);
234        values.putNull(People.DISPLAY_NAME);
235        assertStoredValues(personUri, values);
236    }
237
238    public void testMarkAsContacted() {
239        ContentValues values = new ContentValues();
240        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
241        long personId = ContentUris.parseId(personUri);
242
243        int timesContactedBefore =
244                Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
245        long timeBefore = System.currentTimeMillis();
246        People.markAsContacted(mResolver, personId);
247        long timeAfter = System.currentTimeMillis();
248
249        long lastContacted = Long.parseLong(getStoredValue(personUri, People.LAST_TIME_CONTACTED));
250        int timesContactedAfter =
251            Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
252
253        assertTrue(lastContacted >= timeBefore);
254        assertTrue(lastContacted <= timeAfter);
255        assertEquals(timesContactedAfter, timesContactedBefore + 1);
256    }
257
258    public void testOrganizationsInsert() {
259        ContentValues values = new ContentValues();
260        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
261        long personId = ContentUris.parseId(personUri);
262
263        values.clear();
264        values.put(Organizations.COMPANY, "Sierra");
265        values.put(Organizations.PERSON_ID, personId);
266        values.put(Organizations.TYPE, Organizations.TYPE_CUSTOM);
267        values.put(Organizations.LABEL, "Club");
268        values.put(Organizations.TITLE, "Member");
269        values.put(Organizations.ISPRIMARY, 1);
270
271        Uri uri = mResolver.insert(Organizations.CONTENT_URI, values);
272        assertStoredValues(uri, values);
273        assertSelection(Organizations.CONTENT_URI, values,
274                Organizations._ID, ContentUris.parseId(uri));
275
276        assertPersonIdConstraint(Organizations.CONTENT_URI, Organizations.TYPE,
277                Organizations.TYPE_WORK);
278
279        assertTypeAndLabelConstraints(Organizations.CONTENT_URI, Organizations.PERSON_ID, personId,
280                Organizations.TYPE, Organizations.TYPE_CUSTOM, Organizations.TYPE_OTHER,
281                Organizations.LABEL);
282    }
283
284    public void testPhonesInsert() {
285        ContentValues values = new ContentValues();
286        putContactValues(values);
287        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
288        long personId = ContentUris.parseId(personUri);
289
290        values.clear();
291        values.put(Phones.PERSON_ID, personId);
292        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
293        values.put(Phones.LABEL, "Directory");
294        values.put(Phones.NUMBER, "1-800-4664-411");
295        values.put(Phones.ISPRIMARY, 1);
296
297        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
298
299        // Adding another value to assert
300        values.put(Phones.NUMBER_KEY, "11446640081");
301
302        // The result is joined with People
303        putContactValues(values);
304        assertStoredValues(uri, values);
305        assertSelection(Phones.CONTENT_URI, values,
306                Phones._ID, ContentUris.parseId(uri));
307
308        // Access the phone through People
309        Uri twigUri = Uri.withAppendedPath(personUri, People.Phones.CONTENT_DIRECTORY);
310        assertStoredValues(twigUri, values);
311
312        // Now the person should be joined with Phone
313        values.clear();
314        putContactValues(values);
315        values.put(People.TYPE, Phones.TYPE_CUSTOM);
316        values.put(People.LABEL, "Directory");
317        values.put(People.NUMBER, "1-800-4664-411");
318        assertStoredValues(personUri, values);
319
320        assertPersonIdConstraint(Phones.CONTENT_URI, Phones.TYPE, Phones.TYPE_WORK);
321
322        assertTypeAndLabelConstraints(Phones.CONTENT_URI, Phones.PERSON_ID, personId, Phones.TYPE,
323                Phones.TYPE_CUSTOM, Phones.TYPE_OTHER, Phones.LABEL);
324    }
325
326    public void testPhonesFilterQuery() {
327        ContentValues values = new ContentValues();
328        putContactValues(values);
329        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
330        long personId = ContentUris.parseId(personUri);
331
332        values.clear();
333        values.put(Phones.PERSON_ID, personId);
334        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
335        values.put(Phones.LABEL, "Directory");
336        values.put(Phones.NUMBER, "1-800-4664-411");
337        values.put(Phones.ISPRIMARY, 1);
338
339        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
340
341        Uri filterUri1 = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, "8004664411");
342        assertStoredValues(filterUri1, values);
343
344        Uri filterUri2 = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, "7773334444");
345        assertEquals(0, getCount(filterUri2, null, null));
346    }
347
348
349    public void testEmailInsert() {
350        assertContactMethodInsert(Contacts.KIND_EMAIL, ContactMethods.TYPE_CUSTOM,
351                "Some other way", "foo@acme.com", null, true);
352    }
353
354    public void testImInsert() {
355        assertContactMethodInsert(Contacts.KIND_IM, ContactMethods.TYPE_CUSTOM, "Some other way",
356                "Foo", "Bar", true);
357    }
358
359    public void testPostalInsert() {
360        assertContactMethodInsert(Contacts.KIND_POSTAL, ContactMethods.TYPE_CUSTOM,
361                "Some other way", "Foo", "Bar", true);
362    }
363
364    private void assertContactMethodInsert(int kind, int type, String label, String data,
365            String auxData, boolean primary) {
366        ContentValues values = new ContentValues();
367        putContactValues(values);
368        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
369        long personId = ContentUris.parseId(personUri);
370
371        values.clear();
372        values.put(ContactMethods.PERSON_ID, personId);
373        values.put(ContactMethods.KIND, kind);
374        values.put(ContactMethods.TYPE, type);
375        values.put(ContactMethods.LABEL, label);
376        values.put(ContactMethods.DATA, data);
377        values.put(ContactMethods.AUX_DATA, auxData);
378        values.put(ContactMethods.ISPRIMARY, primary ? 1 : 0);
379
380        Uri uri = mResolver.insert(ContactMethods.CONTENT_URI, values);
381
382        // The result is joined with People
383        putContactValues(values);
384        assertStoredValues(uri, values);
385        assertSelection(ContactMethods.CONTENT_URI, values,
386                ContactMethods._ID, ContentUris.parseId(uri));
387
388        // Access the contact method through People
389        Uri twigUri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
390        assertStoredValues(twigUri, values);
391
392        assertPersonIdConstraint(ContactMethods.CONTENT_URI, ContactMethods.TYPE,
393                ContactMethods.TYPE_WORK);
394
395        assertTypeAndLabelConstraints(ContactMethods.CONTENT_URI, ContactMethods.PERSON_ID,
396                personId, ContactMethods.TYPE, ContactMethods.TYPE_CUSTOM,
397                ContactMethods.TYPE_OTHER, ContactMethods.LABEL);
398    }
399
400    public void testExtensionsInsert() {
401        ContentValues values = new ContentValues();
402        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
403        long personId = ContentUris.parseId(personUri);
404
405        values.clear();
406        values.put(Extensions.PERSON_ID, personId);
407        values.put(Extensions.NAME, "Foo");
408        values.put(Extensions.VALUE, "Bar");
409
410        Uri uri = mResolver.insert(Extensions.CONTENT_URI, values);
411        assertStoredValues(uri, values);
412        assertSelection(Extensions.CONTENT_URI, values,
413                Extensions._ID, ContentUris.parseId(uri));
414
415        // Access the extensions through People
416        Uri twigUri = Uri.withAppendedPath(personUri, People.Extensions.CONTENT_DIRECTORY);
417        assertStoredValues(twigUri, values);
418    }
419
420    public void testGroupsInsert() {
421        ContentValues values = new ContentValues();
422        values.put(Groups.NAME, "Galois");
423        values.put(Groups.NOTES, "Abel");
424        values.put(Groups.SYSTEM_ID, "12345");
425
426        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
427        assertStoredValues(groupUri, values);
428    }
429
430    public void testGroupMembershipsInsert() {
431        ContentValues values = new ContentValues();
432        values.put(Groups.NAME, "Galois");
433        values.put(Groups.NOTES, "Abel");
434        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
435
436        values.clear();
437        values.put(People.NAME, "Klein");
438        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
439
440        long groupId = ContentUris.parseId(groupUri);
441        long personId = ContentUris.parseId(personUri);
442
443        values.clear();
444        values.put(GroupMembership.GROUP_ID, groupId);
445        values.put(GroupMembership.PERSON_ID, personId);
446        Uri membershipUri = mResolver.insert(GroupMembership.CONTENT_URI, values);
447        assertStoredValues(membershipUri, values);
448        assertSelection(GroupMembership.CONTENT_URI, values,
449                GroupMembership._ID, ContentUris.parseId(membershipUri));
450
451        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
452        assertStoredValues(personsGroupsUri, values);
453    }
454
455    public void testAddToGroup() {
456        ContentValues values = new ContentValues();
457        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
458        long personId = ContentUris.parseId(personUri);
459
460        values.clear();
461        values.put(Groups.NAME, "Galois");
462        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
463
464        People.addToGroup(mResolver, personId, "Galois");
465
466        values.clear();
467        values.put(GroupMembership.GROUP_ID, ContentUris.parseId(groupUri));
468        values.put(GroupMembership.PERSON_ID, personId);
469
470        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
471        assertStoredValues(personsGroupsUri, values);
472    }
473
474    // TODO fix and reenable test
475    public void __testPresenceInsertMatchOnHandle() {
476        ContentValues values = new ContentValues();
477        putContactValues(values);
478        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
479        long personId = ContentUris.parseId(personUri);
480
481        String encodedProtocol =
482                ContactMethods.encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
483
484        values.clear();
485        values.put(ContactMethods.PERSON_ID, personId);
486        values.put(ContactMethods.KIND, Contacts.KIND_IM);
487        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
488        values.put(ContactMethods.DATA, "Android");
489        values.put(ContactMethods.AUX_DATA, encodedProtocol);
490        mResolver.insert(ContactMethods.CONTENT_URI, values);
491
492        values.clear();
493        values.put(Presence.IM_PROTOCOL, encodedProtocol);
494        values.put(Presence.IM_HANDLE, "Android");
495        values.put(Presence.IM_ACCOUNT, "foo");
496        values.put(Presence.PRESENCE_STATUS, Presence.OFFLINE);
497        values.put(Presence.PRESENCE_CUSTOM_STATUS, "Coding for Android");
498
499        Uri presenceUri = mResolver.insert(Presence.CONTENT_URI, values);
500        assertSelection(Presence.CONTENT_URI, values,
501                Presence._ID, ContentUris.parseId(presenceUri));
502
503        values.put(Presence.PERSON_ID, personId);
504        assertStoredValues(presenceUri, values);
505
506        // Now the person should be joined with Presence
507        values.clear();
508        putContactValues(values);
509        values.put(People.IM_PROTOCOL, encodedProtocol);
510        values.put(People.IM_HANDLE, "Android");
511        values.put(People.IM_ACCOUNT, "foo");
512        values.put(People.PRESENCE_STATUS, Presence.OFFLINE);
513        values.put(People.PRESENCE_CUSTOM_STATUS, "Coding for Android");
514        assertStoredValues(personUri, values);
515    }
516
517    // TODO fix and reenable test
518    public void __testPresenceInsertMatchOnEmail() {
519        ContentValues values = new ContentValues();
520        putContactValues(values);
521        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
522        long personId = ContentUris.parseId(personUri);
523
524        String protocol =
525            ContactMethods.encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
526
527        values.clear();
528        values.put(ContactMethods.PERSON_ID, personId);
529        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
530        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
531        values.put(ContactMethods.DATA, "Android@android.com");
532        mResolver.insert(ContactMethods.CONTENT_URI, values);
533
534        values.clear();
535        values.put(Presence.IM_PROTOCOL, protocol);
536        values.put(Presence.IM_HANDLE, "Android@android.com");
537        values.put(Presence.IM_ACCOUNT, "foo");
538        values.put(Presence.PRESENCE_STATUS, Presence.OFFLINE);
539        values.put(Presence.PRESENCE_CUSTOM_STATUS, "Coding for Android");
540
541        Uri presenceUri = mResolver.insert(Presence.CONTENT_URI, values);
542
543        // FIXME person_id was not available in legacy ContactsProvider
544        // values.put(Presence.PERSON_ID, personId);
545        assertStoredValues(presenceUri, values);
546        assertSelection(Presence.CONTENT_URI, values,
547                Presence._ID, ContentUris.parseId(presenceUri));
548
549        // Now the person should be joined with Presence
550        values.clear();
551        putContactValues(values);
552        values.put(People.IM_PROTOCOL, protocol);
553        values.put(People.IM_HANDLE, "Android@android.com");
554        values.put(People.IM_ACCOUNT, "foo");
555        values.put(People.PRESENCE_STATUS, Presence.OFFLINE);
556        values.put(People.PRESENCE_CUSTOM_STATUS, "Coding for Android");
557        assertStoredValues(personUri, values);
558    }
559
560    public void testPhotoUpdate() throws Exception {
561        byte[] photo = loadTestPhoto();
562
563        ContentValues values = new ContentValues();
564        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
565
566        values.clear();
567        values.put(Photos.DATA, photo);
568        values.put(Photos.LOCAL_VERSION, "10");
569        // FIXME this column was unavailable for update in legacy ContactsProvider
570        // values.put(Photos.DOWNLOAD_REQUIRED, 1);
571        values.put(Photos.EXISTS_ON_SERVER, 1);
572        values.put(Photos.SYNC_ERROR, "404 does not exist");
573
574        Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
575        mResolver.update(photoUri, values, null, null);
576        assertStoredValues(photoUri, values);
577    }
578
579    /**
580     * Capturing the search suggestion requirements in test cases as a reference.
581     */
582    public void testSearchSuggestionsNotInMyContacts() throws Exception {
583
584        // We don't provide compatibility for search suggestions
585        if (!USE_LEGACY_PROVIDER) {
586            return;
587        }
588
589        ContentValues values = new ContentValues();
590        putContactValues(values);
591        mResolver.insert(People.CONTENT_URI, values);
592
593        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
594                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("D").build();
595
596        // If the contact is not in the "my contacts" group, nothing should be found
597        Cursor c = mResolver.query(searchUri, null, null, null, null);
598        assertEquals(0, c.getCount());
599        c.close();
600    }
601
602    /**
603     * Capturing the search suggestion requirements in test cases as a reference.
604     */
605    public void testSearchSuggestionsByName() throws Exception {
606
607        // We don't provide compatibility for search suggestions
608        if (!USE_LEGACY_PROVIDER) {
609            return;
610        }
611
612        assertSearchSuggestion(
613                true,  // name
614                true,  // photo
615                true,  // organization
616                false, // phone
617                false, // email
618                "D",   // query
619                true,  // expect icon URI
620                null, "Deer Dough", "Google");
621
622        assertSearchSuggestion(
623                true,  // name
624                true,  // photo
625                false, // organization
626                true,  // phone
627                false, // email
628                "D",   // query
629                true,  // expect icon URI
630                null, "Deer Dough", "1-800-4664-411");
631
632        assertSearchSuggestion(
633                true,  // name
634                true,  // photo
635                false, // organization
636                false, // phone
637                true,  // email
638                "D",   // query
639                true,  // expect icon URI
640                String.valueOf(Presence.getPresenceIconResourceId(Presence.OFFLINE)),
641                "Deer Dough", "foo@acme.com");
642
643        assertSearchSuggestion(
644                true,  // name
645                false, // photo
646                true,  // organization
647                false, // phone
648                false, // email
649                "D",   // query
650                false, // expect icon URI
651                null, "Deer Dough", "Google");
652    }
653
654    private void assertSearchSuggestion(boolean name, boolean photo, boolean organization,
655            boolean phone, boolean email, String query, boolean expectIcon1Uri, String expectedIcon2,
656            String expectedText1, String expectedText2) throws IOException {
657        ContentValues values = new ContentValues();
658
659        if (name) {
660            values.put(People.NAME, "Deer Dough");
661        }
662
663        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
664        long personId = ContentUris.parseId(personUri);
665
666        People.addToMyContactsGroup(mResolver, personId);
667
668        if (photo) {
669            values.clear();
670            byte[] photoData = loadTestPhoto();
671            values.put(Photos.DATA, photoData);
672            values.put(Photos.LOCAL_VERSION, "1");
673            values.put(Photos.EXISTS_ON_SERVER, 0);
674            Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
675            mResolver.update(photoUri, values, null, null);
676        }
677
678        if (organization) {
679            values.clear();
680            values.put(Organizations.ISPRIMARY, 1);
681            values.put(Organizations.COMPANY, "Google");
682            values.put(Organizations.TYPE, Organizations.TYPE_WORK);
683            values.put(Organizations.PERSON_ID, personId);
684            mResolver.insert(Organizations.CONTENT_URI, values);
685        }
686
687        if (email) {
688            values.clear();
689            values.put(ContactMethods.PERSON_ID, personId);
690            values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
691            values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
692            values.put(ContactMethods.DATA, "foo@acme.com");
693            values.put(ContactMethods.ISPRIMARY, 1);
694            mResolver.insert(ContactMethods.CONTENT_URI, values);
695
696
697            String protocol = ContactMethods
698                    .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
699            values.clear();
700            values.put(Presence.IM_PROTOCOL, protocol);
701            values.put(Presence.IM_HANDLE, "foo@acme.com");
702            values.put(Presence.IM_ACCOUNT, "foo");
703            values.put(Presence.PRESENCE_STATUS, Presence.OFFLINE);
704            values.put(Presence.PRESENCE_CUSTOM_STATUS, "Coding for Android");
705            mResolver.insert(Presence.CONTENT_URI, values);
706        }
707
708        if (phone) {
709            values.clear();
710            values.put(Phones.PERSON_ID, personId);
711            values.put(Phones.TYPE, Phones.TYPE_HOME);
712            values.put(Phones.NUMBER, "1-800-4664-411");
713            values.put(Phones.ISPRIMARY, 1);
714            mResolver.insert(Phones.CONTENT_URI, values);
715        }
716
717        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
718                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath(query).build();
719
720        Cursor c = mResolver.query(searchUri, null, null, null, null);
721        assertEquals(1, c.getCount());
722        c.moveToFirst();
723        values.clear();
724
725        String icon1 = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1));
726        if (expectIcon1Uri) {
727            assertTrue(icon1.startsWith("content:"));
728        } else {
729            assertEquals(String.valueOf(com.android.internal.R.drawable.ic_contact_picture), icon1);
730        }
731
732        // SearchManager does not declare a constant for _id
733        values.put("_id", personId);
734        values.put(SearchManager.SUGGEST_COLUMN_ICON_2, expectedIcon2);
735        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, personId);
736        values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, personId);
737        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, expectedText1);
738        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, expectedText2);
739        assertCursorValues(c, values);
740        c.close();
741
742        // Cleanup
743        mResolver.delete(personUri, null, null);
744    }
745
746    /**
747     * Capturing the search suggestion requirements in test cases as a reference.
748     */
749    public void testSearchSuggestionsByPhoneNumber() throws Exception {
750
751        // We don't provide compatibility for search suggestions
752        if (!USE_LEGACY_PROVIDER) {
753            return;
754        }
755
756        ContentValues values = new ContentValues();
757
758        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
759                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("12345").build();
760
761        Cursor c = mResolver.query(searchUri, null, null, null, null);
762        assertEquals(2, c.getCount());
763        c.moveToFirst();
764
765        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Execute");
766        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "");
767        values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
768                String.valueOf(com.android.internal.R.drawable.call_contact));
769        values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
770                Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
771        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345");
772        values.putNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
773        assertCursorValues(c, values);
774
775        c.moveToNext();
776        values.clear();
777        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Dial number");
778        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using 12345");
779        values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
780                String.valueOf(com.android.internal.R.drawable.create_contact));
781        values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
782                Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
783        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345");
784        values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
785                SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
786        assertCursorValues(c, values);
787        c.close();
788    }
789
790    private void putContactValues(ContentValues values) {
791        // Populating only unhidden columns
792        values.put(People.NAME, "Deer Dough");
793        values.put(People.PHONETIC_NAME, "Dear Doe");
794        values.put(People.NOTES, "Cash Cow");
795        values.put(People.TIMES_CONTACTED, 3);
796        values.put(People.LAST_TIME_CONTACTED, 10);
797        values.put(People.CUSTOM_RINGTONE, "ringtone");
798        values.put(People.SEND_TO_VOICEMAIL, 1);
799        values.put(People.STARRED, 1);
800    }
801
802    private void assertFilteredContacts(String filter, String... expectedNames) {
803        Uri filterUri = Uri.withAppendedPath(People.CONTENT_FILTER_URI, filter);
804        Cursor c = mResolver.query(filterUri, null, null, null, null);
805        try {
806            assertEquals("Record count", expectedNames.length, c.getCount());
807            int column = c.getColumnIndex(People.NAME);
808            for (int i = 0; i < expectedNames.length; i++) {
809                c.moveToNext();
810                assertEquals(expectedNames[i], c.getString(column));
811            }
812        } finally {
813            c.close();
814        }
815    }
816
817    private void assertPersonIdConstraint(Uri uri, String typeColumn, int defaultType) {
818        ContentValues values = new ContentValues();
819        values.put(typeColumn, defaultType);
820        try {
821            mResolver.insert(uri, values);
822            fail("Inserted row without person ID");
823        } catch (Exception e) {
824            // Exception expected
825        }
826    }
827
828    private void assertTypeAndLabelConstraints(Uri uri, String personIdColumn, long personId,
829            String typeColumn, int defaultType, int otherType, String labelColumn) {
830        ContentValues values = new ContentValues();
831        values.put(personIdColumn, personId);
832        values.put(typeColumn, defaultType);
833        try {
834            mResolver.insert(uri, values);
835            fail("Inserted row with custom type but without label");
836        } catch (Exception e) {
837            // Exception expected
838        }
839
840        values.clear();
841        values.put(personIdColumn, personId);
842        try {
843            mResolver.insert(uri, values);
844            fail("Inserted row without either type or label");
845        } catch (Exception e) {
846            // Exception expected
847        }
848
849        values.clear();
850        values.put(personIdColumn, personId);
851        values.put(typeColumn, otherType);
852        values.put(labelColumn, "Foo");
853        try {
854            mResolver.insert(uri, values);
855            fail("Inserted row with both type and label");
856        } catch (Exception e) {
857            // Exception expected
858        }
859    }
860
861    @Override
862    protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) {
863        if (USE_LEGACY_PROVIDER) {
864            // A bug in the legacy ContactsProvider prevents us from using the
865            // _id column in selection.
866            super.assertSelection(uri, values, null, 0);
867        } else {
868            values.put(idColumn, id);
869            super.assertSelection(uri, values, idColumn, id);
870        }
871    }
872}
873