LegacyContactsProviderTest.java revision 71e051c79a57af70ec7b095074c3e7faf9507b52
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", People._ID, ContentUris.parseId(uri));
65    }
66
67    public void testPeopleUpdate() {
68        ContentValues values = new ContentValues();
69        putContactValues(values);
70
71        Uri uri = mResolver.insert(People.CONTENT_URI, values);
72        long personId = ContentUris.parseId(uri);
73        assertStoredValues(uri, values);
74        assertSelection(People.CONTENT_URI, values, "people", People._ID, personId);
75
76        values.clear();
77        putContactValues2(values);
78        mResolver.update(uri, values, null, null);
79        assertStoredValues(uri, values);
80
81        values.clear();
82        putContactValues(values);
83        mResolver.update(People.CONTENT_URI, values, People._ID + "=" + personId, null);
84        assertStoredValues(uri, values);
85    }
86
87    public void testPeopleDelete() {
88        ContentValues values = new ContentValues();
89        values.put(People.NAME, "John Doe");
90        Uri personId = mResolver.insert(People.CONTENT_URI, values);
91        mResolver.delete(personId, null, null);
92
93        Cursor c = mResolver.query(personId, null, People.NAME + "='John Doe'" , null, null);
94        assertEquals("Record count after deletion", 0, c.getCount());
95        c.close();
96
97        try {
98            mResolver.query(People.DELETED_CONTENT_URI, null, null, null, null);
99        } catch (UnsupportedOperationException e) {
100            // Expected exception
101        }
102    }
103
104    public void testPeopleFilter() {
105        ContentValues values = new ContentValues();
106        values.put(People.NAME, "Deer Doe");
107        mResolver.insert(People.CONTENT_URI, values);
108
109        values.clear();
110        values.put(People.NAME, "Dear Dough");
111        mResolver.insert(People.CONTENT_URI, values);
112
113        values.clear();
114        values.put(People.NAME, "D.R. Dauwe");
115        mResolver.insert(People.CONTENT_URI, values);
116
117        assertFilteredContacts("d", "Deer Doe", "Dear Dough", "D.R. Dauwe");
118        assertFilteredContacts("de", "Deer Doe", "Dear Dough");
119        assertFilteredContacts("dee", "Deer Doe");
120        assertFilteredContacts("der");
121    }
122
123    public void testDefaultDisplayName() {
124        ContentValues values = new ContentValues();
125        values.put(People.NAME, "John Doe");
126        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
127        assertStoredValue(personUri, People.DISPLAY_NAME, "John Doe");
128    }
129
130    public void testPrimaryOrganization() {
131        ContentValues values = new ContentValues();
132        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
133        long personId = ContentUris.parseId(personUri);
134
135        // Primary
136        values.clear();
137        values.put(Organizations.ISPRIMARY, 1);
138        values.put(Organizations.COMPANY, "Google");
139        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
140        values.put(Organizations.PERSON_ID, personId);
141        Uri orgUri1 = mResolver.insert(Organizations.CONTENT_URI, values);
142
143        // Non-primary
144        values.clear();
145        values.put(Organizations.COMPANY, "Acme");
146        values.put(Organizations.TYPE, Organizations.TYPE_WORK);
147        values.put(Organizations.PERSON_ID, personId);
148        Uri orgUri2 = mResolver.insert(Organizations.CONTENT_URI, values);
149
150        values.clear();
151        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri1));
152        values.put(People.DISPLAY_NAME, "Google");
153        assertStoredValues(personUri, values);
154
155        // Remove the original primary organization
156        mResolver.delete(orgUri1, null, null);
157
158        values.clear();
159        values.put(People.PRIMARY_ORGANIZATION_ID, ContentUris.parseId(orgUri2));
160        values.put(People.DISPLAY_NAME, "Acme");
161        assertStoredValues(personUri, values);
162
163        // Remove the remaining organization
164        mResolver.delete(orgUri2, null, null);
165
166        values.clear();
167        values.putNull(People.PRIMARY_ORGANIZATION_ID);
168        values.putNull(People.DISPLAY_NAME);
169        assertStoredValues(personUri, values);
170    }
171
172    public void testPrimaryPhone() {
173        ContentValues values = new ContentValues();
174        putContactValuesExceptName(values);
175
176        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
177        long personId = ContentUris.parseId(personUri);
178
179        // Primary
180        values.clear();
181        values.put(Phones.ISPRIMARY, 1);
182        values.put(Phones.TYPE, Phones.TYPE_WORK);
183        values.put(Phones.PERSON_ID, personId);
184        values.put(Phones.NUMBER, "12345");
185        Uri phoneUri1 = mResolver.insert(Phones.CONTENT_URI, values);
186
187        // Non-primary
188        values.clear();
189        values.put(Phones.TYPE, Phones.TYPE_WORK);
190        values.put(Phones.PERSON_ID, personId);
191        values.put(Phones.NUMBER, "67890");
192        Uri phoneUri2 = mResolver.insert(Phones.CONTENT_URI, values);
193
194        values.clear();
195        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri1));
196        values.put(People.DISPLAY_NAME, "12345");
197        assertStoredValues(personUri, values);
198
199        values.clear();
200        putContactValuesExceptName(values);
201        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri1));
202        assertStoredValues(phoneUri2, values);
203
204        // Remove the primary phone number
205        mResolver.delete(phoneUri1, null, null);
206
207        values.clear();
208        values.put(People.PRIMARY_PHONE_ID, ContentUris.parseId(phoneUri2));
209        values.put(People.DISPLAY_NAME, "67890");
210        assertStoredValues(personUri, values);
211
212        // Remove the remaining phone number
213        mResolver.delete(phoneUri2, null, null);
214
215        values.clear();
216        values.putNull(People.PRIMARY_PHONE_ID);
217        values.putNull(People.DISPLAY_NAME);
218        assertStoredValues(personUri, values);
219    }
220
221    public void testEmailCrud() {
222        ContentValues values = new ContentValues();
223        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
224        long personId = ContentUris.parseId(personUri);
225
226        // Primary
227        values.clear();
228        values.put(ContactMethods.PERSON_ID, personId);
229        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
230        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
231        values.put(ContactMethods.DATA, "foo@acme.com");
232        values.put(ContactMethods.ISPRIMARY, 1);
233        Uri emailUri1 = mResolver.insert(ContactMethods.CONTENT_URI, values);
234
235        assertEquals(ContactMethods.CONTENT_EMAIL_ITEM_TYPE, mResolver.getType(emailUri1));
236
237        assertStoredValues(ContactMethods.CONTENT_URI,
238                ContactMethods._ID + "=" + ContentUris.parseId(emailUri1), null, values);
239        assertStoredValues(ContactMethods.CONTENT_EMAIL_URI,
240                ContactMethods._ID + "=" + ContentUris.parseId(emailUri1), null, values);
241    }
242
243    public void testPrimaryEmail() {
244        ContentValues values = new ContentValues();
245        putContactValuesExceptName(values);
246        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
247        long personId = ContentUris.parseId(personUri);
248
249        // Primary
250        values.clear();
251        values.put(ContactMethods.PERSON_ID, personId);
252        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
253        values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
254        values.put(ContactMethods.DATA, "foo@acme.com");
255        values.put(ContactMethods.ISPRIMARY, 1);
256        Uri emailUri1 = mResolver.insert(ContactMethods.CONTENT_URI, values);
257
258        // Non-primary
259        values.clear();
260        values.put(ContactMethods.PERSON_ID, personId);
261        values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
262        values.put(ContactMethods.TYPE, ContactMethods.TYPE_WORK);
263        values.put(ContactMethods.DATA, "bar@acme.com");
264        Uri emailUri2 = mResolver.insert(ContactMethods.CONTENT_URI, values);
265
266        values.clear();
267        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri1));
268        values.put(People.DISPLAY_NAME, "foo@acme.com");
269        assertStoredValues(personUri, values);
270
271        values.clear();
272        putContactValuesExceptName(values);
273        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri1));
274        assertStoredValues(emailUri2, values);
275
276        // Remove the primary email
277        mResolver.delete(emailUri1, null, null);
278
279        values.clear();
280        values.put(People.PRIMARY_EMAIL_ID, ContentUris.parseId(emailUri2));
281        values.put(People.DISPLAY_NAME, "bar@acme.com");
282        assertStoredValues(personUri, values);
283
284        // Remove the remaining email
285        mResolver.delete(emailUri2, null, null);
286
287        values.clear();
288        values.putNull(People.PRIMARY_EMAIL_ID);
289        values.putNull(People.DISPLAY_NAME);
290        assertStoredValues(personUri, values);
291    }
292
293    public void testMarkAsContacted() {
294        ContentValues values = new ContentValues();
295        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
296        long personId = ContentUris.parseId(personUri);
297
298        int timesContactedBefore =
299                Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
300        long timeBefore = System.currentTimeMillis();
301        People.markAsContacted(mResolver, personId);
302        long timeAfter = System.currentTimeMillis();
303
304        long lastContacted = Long.parseLong(getStoredValue(personUri, People.LAST_TIME_CONTACTED));
305        int timesContactedAfter =
306            Integer.parseInt(getStoredValue(personUri, People.TIMES_CONTACTED));
307
308        assertTrue(lastContacted >= timeBefore);
309        assertTrue(lastContacted <= timeAfter);
310        assertEquals(timesContactedAfter, timesContactedBefore + 1);
311    }
312
313    public void testOrganizationsInsert() {
314        ContentValues values = new ContentValues();
315        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
316        long personId = ContentUris.parseId(personUri);
317
318        values.clear();
319        values.put(Organizations.COMPANY, "Sierra");
320        values.put(Organizations.PERSON_ID, personId);
321        values.put(Organizations.TYPE, Organizations.TYPE_CUSTOM);
322        values.put(Organizations.LABEL, "Club");
323        values.put(Organizations.TITLE, "Member");
324        values.put(Organizations.ISPRIMARY, 1);
325
326        Uri uri = mResolver.insert(Organizations.CONTENT_URI, values);
327        assertStoredValues(uri, values);
328        assertSelection(Organizations.CONTENT_URI, values,
329                "organizations", Organizations._ID, ContentUris.parseId(uri));
330
331        assertPersonIdConstraint(Organizations.CONTENT_URI, Organizations.TYPE,
332                Organizations.TYPE_WORK);
333
334        assertTypeAndLabelConstraints(Organizations.CONTENT_URI, Organizations.PERSON_ID, personId,
335                Organizations.TYPE, Organizations.TYPE_CUSTOM, Organizations.TYPE_OTHER,
336                Organizations.LABEL);
337    }
338
339    public void testOrganizationsUpdate() {
340        ContentValues values = new ContentValues();
341        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
342        long personId = ContentUris.parseId(personUri);
343
344        values.clear();
345        values.put(Organizations.COMPANY, "Sierra");
346        values.put(Organizations.PERSON_ID, personId);
347        values.put(Organizations.TYPE, Organizations.TYPE_CUSTOM);
348        values.put(Organizations.LABEL, "Club");
349        values.put(Organizations.TITLE, "Member");
350        values.put(Organizations.ISPRIMARY, 0);
351
352        Uri uri = mResolver.insert(Organizations.CONTENT_URI, values);
353
354        values.clear();
355        values.put(Organizations.COMPANY, "Planetary");
356        values.put(Organizations.PERSON_ID, personId);
357        values.put(Organizations.TYPE, Organizations.TYPE_CUSTOM);
358        values.put(Organizations.LABEL, "Society");
359        values.put(Organizations.TITLE, "Chair");
360        values.put(Organizations.ISPRIMARY, 1);
361
362        mResolver.update(uri, values, null, null);
363
364        assertStoredValues(uri, values);
365    }
366
367    public void testPhonesInsert() {
368        ContentValues values = new ContentValues();
369        putContactValues(values);
370        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
371        long personId = ContentUris.parseId(personUri);
372
373        values.clear();
374        values.put(Phones.PERSON_ID, personId);
375        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
376        values.put(Phones.LABEL, "Directory");
377        values.put(Phones.NUMBER, "1-800-4664-411");
378        values.put(Phones.ISPRIMARY, 1);
379
380        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
381
382        // Adding another value to assert
383        values.put(Phones.NUMBER_KEY, "11446640081");
384
385        // The result is joined with People
386        putContactValues(values);
387        assertStoredValues(uri, values);
388        assertSelection(Phones.CONTENT_URI, values, "phones",
389                Phones._ID, ContentUris.parseId(uri));
390
391        // Access the phone through People
392        Uri twigUri = Uri.withAppendedPath(personUri, People.Phones.CONTENT_DIRECTORY);
393        assertStoredValues(twigUri, values);
394
395        // Now the person should be joined with Phone
396        values.clear();
397        putContactValues(values);
398        values.put(People.TYPE, Phones.TYPE_CUSTOM);
399        values.put(People.LABEL, "Directory");
400        values.put(People.NUMBER, "1-800-4664-411");
401        assertStoredValues(personUri, values);
402
403        assertPersonIdConstraint(Phones.CONTENT_URI, Phones.TYPE, Phones.TYPE_WORK);
404
405        assertTypeAndLabelConstraints(Phones.CONTENT_URI, Phones.PERSON_ID, personId, Phones.TYPE,
406                Phones.TYPE_CUSTOM, Phones.TYPE_OTHER, Phones.LABEL);
407    }
408
409    public void testPhonesUpdate() {
410        ContentValues values = new ContentValues();
411        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
412        long personId = ContentUris.parseId(personUri);
413
414        values.clear();
415        values.put(Phones.PERSON_ID, personId);
416        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
417        values.put(Phones.LABEL, "Directory");
418        values.put(Phones.NUMBER, "1-800-4664-411");
419        values.put(Phones.ISPRIMARY, 0);
420
421        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
422
423        values.clear();
424        values.put(Phones.PERSON_ID, personId);
425        values.put(Phones.TYPE, Phones.TYPE_FAX_HOME);
426        values.putNull(Phones.LABEL);
427        values.put(Phones.NUMBER, "1-800-555-4663");
428        values.put(Phones.ISPRIMARY, 1);
429
430        mResolver.update(uri, values, null, null);
431
432        assertStoredValues(uri, values);
433    }
434
435    public void testPhonesFilterQuery() {
436        ContentValues values = new ContentValues();
437        putContactValues(values);
438        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
439        long personId = ContentUris.parseId(personUri);
440
441        values.clear();
442        values.put(Phones.PERSON_ID, personId);
443        values.put(Phones.TYPE, Phones.TYPE_CUSTOM);
444        values.put(Phones.LABEL, "Directory");
445        values.put(Phones.NUMBER, "1-800-4664-411");
446        values.put(Phones.ISPRIMARY, 1);
447
448        Uri uri = mResolver.insert(Phones.CONTENT_URI, values);
449
450        Uri filterUri1 = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, "8004664411");
451        assertStoredValues(filterUri1, values);
452
453        Uri filterUri2 = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, "7773334444");
454        assertEquals(0, getCount(filterUri2, null, null));
455    }
456
457    public void testEmailInsert() {
458        assertContactMethodInsert(Contacts.KIND_EMAIL,
459                ContactMethods.TYPE_CUSTOM, "Some other way", "foo@acme.com", null, true);
460    }
461
462    public void testEmailUpdate() {
463        assertContactMethodUpdate(Contacts.KIND_EMAIL,
464                ContactMethods.TYPE_CUSTOM, "Some other way", "foo@acme.com", null, false,
465                ContactMethods.TYPE_HOME, null, "bar@acme.com", "aux", true);
466    }
467
468    public void testImInsertStandard() {
469        assertContactMethodInsert(Contacts.KIND_IM,
470                ContactMethods.TYPE_CUSTOM, "Some other way", "pre:3", "Bar", true);
471    }
472
473    public void testImUpdateStandard() {
474        assertContactMethodUpdate(Contacts.KIND_IM,
475                ContactMethods.TYPE_CUSTOM, "Some other way", "pre:3", "Bar", false,
476                ContactMethods.TYPE_WORK, null, "pre:4", "Buz", true);
477    }
478
479    public void testImInsertCustom() {
480        assertContactMethodInsert(Contacts.KIND_IM,
481                ContactMethods.TYPE_CUSTOM, "Some other way", "custom:my_proto", "Bar", true);
482    }
483
484    public void testImUpdateCustom() {
485        assertContactMethodUpdate(Contacts.KIND_IM,
486                ContactMethods.TYPE_CUSTOM, "Some other way", "custom:my_proto", "Bar", false,
487                ContactMethods.TYPE_HOME, null, "custom:my_other_proto", "Buz", true);
488    }
489
490    public void testPostalInsert() {
491        assertContactMethodInsert(Contacts.KIND_POSTAL,
492                ContactMethods.TYPE_CUSTOM, "Some other way", "Foo", "Bar", true);
493    }
494
495    public void testPostalUpdate() {
496        assertContactMethodUpdate(Contacts.KIND_POSTAL,
497                ContactMethods.TYPE_CUSTOM, "Some other way", "Foo", "Bar", false,
498                ContactMethods.TYPE_WORK, null, "Biz", "Baz", true);
499    }
500
501    private void assertContactMethodInsert(int kind, int type, String label, String data,
502            String auxData, boolean primary) {
503        ContentValues values = new ContentValues();
504        putContactValues(values);
505        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
506        long personId = ContentUris.parseId(personUri);
507
508        values.clear();
509        values.put(ContactMethods.PERSON_ID, personId);
510        values.put(ContactMethods.KIND, kind);
511        values.put(ContactMethods.TYPE, type);
512        values.put(ContactMethods.LABEL, label);
513        values.put(ContactMethods.DATA, data);
514        values.put(ContactMethods.AUX_DATA, auxData);
515        values.put(ContactMethods.ISPRIMARY, primary ? 1 : 0);
516
517        Uri uri = mResolver.insert(ContactMethods.CONTENT_URI, values);
518
519        // The result is joined with People
520        putContactValues(values);
521        assertStoredValues(uri, values);
522        assertSelection(ContactMethods.CONTENT_URI, values, "contact_methods",
523                ContactMethods._ID, ContentUris.parseId(uri));
524
525        // Access the contact method through People
526        Uri twigUri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
527        assertStoredValues(twigUri, values);
528
529        assertPersonIdConstraint(ContactMethods.CONTENT_URI, ContactMethods.TYPE,
530                ContactMethods.TYPE_WORK);
531
532        assertTypeAndLabelConstraints(ContactMethods.CONTENT_URI, ContactMethods.PERSON_ID,
533                personId, ContactMethods.TYPE, ContactMethods.TYPE_CUSTOM,
534                ContactMethods.TYPE_OTHER, ContactMethods.LABEL);
535    }
536
537    private void assertContactMethodUpdate(int kind,
538            int type1, String label1, String data1, String auxData1, boolean primary1,
539            int type2, String label2, String data2, String auxData2, boolean primary2) {
540        ContentValues values = new ContentValues();
541        putContactValues(values);
542        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
543        long personId = ContentUris.parseId(personUri);
544
545        values.clear();
546        values.put(ContactMethods.PERSON_ID, personId);
547        values.put(ContactMethods.KIND, kind);
548        values.put(ContactMethods.TYPE, type1);
549        values.put(ContactMethods.LABEL, label1);
550        values.put(ContactMethods.DATA, data1);
551        values.put(ContactMethods.AUX_DATA, auxData1);
552        values.put(ContactMethods.ISPRIMARY, primary1 ? 1 : 0);
553
554        Uri uri = mResolver.insert(ContactMethods.CONTENT_URI, values);
555
556        values.clear();
557        values.put(ContactMethods.TYPE, type2);
558        values.put(ContactMethods.LABEL, label2);
559        values.put(ContactMethods.DATA, data2);
560        values.put(ContactMethods.AUX_DATA, auxData2);
561        values.put(ContactMethods.ISPRIMARY, primary2 ? 1 : 0);
562        mResolver.update(uri, values, null, null);
563
564        assertStoredValues(uri, values);
565    }
566
567    public void testExtensionsInsert() {
568        ContentValues values = new ContentValues();
569        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
570        long personId = ContentUris.parseId(personUri);
571
572        values.clear();
573        values.put(Extensions.PERSON_ID, personId);
574        values.put(Extensions.NAME, "Foo");
575        values.put(Extensions.VALUE, "Bar");
576
577        Uri uri = mResolver.insert(Extensions.CONTENT_URI, values);
578        assertStoredValues(uri, values);
579        assertSelection(Extensions.CONTENT_URI, values, "extensions",
580                Extensions._ID, ContentUris.parseId(uri));
581
582        // Access the extensions through People
583        Uri twigUri = Uri.withAppendedPath(personUri, People.Extensions.CONTENT_DIRECTORY);
584        assertStoredValues(twigUri, values);
585    }
586
587    public void testExtensionsUpdate() {
588        ContentValues values = new ContentValues();
589        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
590        long personId = ContentUris.parseId(personUri);
591
592        values.clear();
593        values.put(Extensions.PERSON_ID, personId);
594        values.put(Extensions.NAME, "Foo");
595        values.put(Extensions.VALUE, "Bar");
596
597        Uri uri = mResolver.insert(Extensions.CONTENT_URI, values);
598
599        values.clear();
600        values.put(Extensions.NAME, "Biz");
601        values.put(Extensions.VALUE, "Baz");
602        mResolver.update(uri, values, null, null);
603
604        assertStoredValues(uri, values);
605    }
606
607    public void testGroupsInsert() {
608        ContentValues values = new ContentValues();
609        values.put(Groups.NAME, "Galois");
610        values.put(Groups.NOTES, "Abel");
611        values.put(Groups.SYSTEM_ID, "12345");
612
613        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
614        assertStoredValues(groupUri, values);
615    }
616
617    public void testGroupsUpdate() {
618        ContentValues values = new ContentValues();
619        values.put(Groups.NAME, "Galois");
620        values.put(Groups.NOTES, "Abel");
621        values.put(Groups.SYSTEM_ID, "12345");
622
623        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
624
625        values.clear();
626        values.put(Groups.NAME, "Klein");
627        values.put(Groups.NOTES, "Vierergruppe");
628        values.put(Groups.SYSTEM_ID, "1234");
629        mResolver.update(groupUri, values, null, null);
630        assertStoredValues(groupUri, values);
631    }
632
633    public void testGroupMembershipsInsert() {
634        ContentValues values = new ContentValues();
635        values.put(Groups.NOTES, "Abel");
636        Uri groupUri = insertLegacyGroup("Galois", values);
637        Uri personUri = insertPerson("Klein", values);
638        Uri membershipUri = insertLegacyGroupMembership(groupUri, personUri, values);
639        assertStoredValues(membershipUri, values);
640        assertSelection(GroupMembership.CONTENT_URI, values, "groupmembership",
641                GroupMembership._ID, ContentUris.parseId(membershipUri));
642
643        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
644        assertStoredValues(personsGroupsUri, values);
645    }
646
647    public void testAddToGroup() {
648        ContentValues values = new ContentValues();
649        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
650        long personId = ContentUris.parseId(personUri);
651
652        values.clear();
653        values.put(Groups.NAME, "Galois");
654        Uri groupUri = mResolver.insert(Groups.CONTENT_URI, values);
655
656        People.addToGroup(mResolver, personId, "Galois");
657
658        values.clear();
659        values.put(GroupMembership.GROUP_ID, ContentUris.parseId(groupUri));
660        values.put(GroupMembership.PERSON_ID, personId);
661
662        Uri personsGroupsUri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
663        assertStoredValues(personsGroupsUri, values);
664    }
665
666    public void testGroupMembersByGroupName() {
667        ContentValues values = new ContentValues();
668        Uri groupUri1 = insertLegacyGroup("Galois", values);
669        Uri personUri1 = insertPerson("Klein", values);
670        insertLegacyGroupMembership(groupUri1, personUri1, values);
671
672        Uri groupUri2 = insertLegacyGroup("Euler", values);
673        Uri personUri2 = insertPerson("Lagrange", values);
674        insertLegacyGroupMembership(groupUri2, personUri2, values);
675
676        // NOTE: testing non-public API support
677        assertStoredValue(Uri.parse("content://contacts/groups/name/Galois/members"),
678                People.NAME, "Klein");
679        assertStoredValue(Uri.parse("content://contacts/groups/name/Euler/members"),
680                People.NAME, "Lagrange");
681    }
682
683    public void testPhotoUpdate() throws Exception {
684        byte[] photo = loadTestPhoto();
685
686        ContentValues values = new ContentValues();
687        Uri personUri = mResolver.insert(People.CONTENT_URI, values);
688
689        values.clear();
690        values.put(Photos.DATA, photo);
691        values.put(Photos.LOCAL_VERSION, "10");
692        // FIXME this column was unavailable for update in legacy ContactsProvider
693        // values.put(Photos.DOWNLOAD_REQUIRED, 1);
694        values.put(Photos.EXISTS_ON_SERVER, 1);
695        values.put(Photos.SYNC_ERROR, "404 does not exist");
696
697        Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
698        mResolver.update(photoUri, values, null, null);
699        assertStoredValues(photoUri, values);
700
701        long photoId = Long.parseLong(getStoredValue(photoUri, Photos._ID));
702
703        values.put(Photos.LOCAL_VERSION, "11");
704        Uri contentUri = ContentUris.withAppendedId(Photos.CONTENT_URI, photoId);
705        mResolver.update(contentUri, values, null, null);
706        assertStoredValues(contentUri, values);
707        assertStoredValues(photoUri, values);
708    }
709
710    /**
711     * Capturing the search suggestion requirements in test cases as a reference.
712     */
713    public void testSearchSuggestionsNotInMyContacts() throws Exception {
714
715        // We don't provide compatibility for search suggestions
716        if (!USE_LEGACY_PROVIDER) {
717            return;
718        }
719
720        ContentValues values = new ContentValues();
721        putContactValues(values);
722        mResolver.insert(People.CONTENT_URI, values);
723
724        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
725                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("D").build();
726
727        // If the contact is not in the "my contacts" group, nothing should be found
728        Cursor c = mResolver.query(searchUri, null, null, null, null);
729        assertEquals(0, c.getCount());
730        c.close();
731    }
732
733    /**
734     * Capturing the search suggestion requirements in test cases as a reference.
735     */
736    public void testSearchSuggestionsByName() throws Exception {
737
738        // We don't provide compatibility for search suggestions
739        if (!USE_LEGACY_PROVIDER) {
740            return;
741        }
742
743        assertSearchSuggestion(
744                true,  // name
745                true,  // photo
746                true,  // organization
747                false, // phone
748                false, // email
749                "D",   // query
750                true,  // expect icon URI
751                null, "Deer Dough", "Google");
752
753        assertSearchSuggestion(
754                true,  // name
755                true,  // photo
756                false, // organization
757                true,  // phone
758                false, // email
759                "D",   // query
760                true,  // expect icon URI
761                null, "Deer Dough", "1-800-4664-411");
762
763        assertSearchSuggestion(
764                true,  // name
765                true,  // photo
766                false, // organization
767                false, // phone
768                true,  // email
769                "D",   // query
770                true,  // expect icon URI
771                String.valueOf(Presence.getPresenceIconResourceId(Presence.OFFLINE)),
772                "Deer Dough", "foo@acme.com");
773
774        assertSearchSuggestion(
775                true,  // name
776                false, // photo
777                true,  // organization
778                false, // phone
779                false, // email
780                "D",   // query
781                false, // expect icon URI
782                null, "Deer Dough", "Google");
783    }
784
785    private void assertSearchSuggestion(boolean name, boolean photo, boolean organization,
786            boolean phone, boolean email, String query, boolean expectIcon1Uri, String expectedIcon2,
787            String expectedText1, String expectedText2) throws IOException {
788        ContentValues values = new ContentValues();
789
790        if (name) {
791            values.put(People.NAME, "Deer Dough");
792        }
793
794        final Uri personUri = mResolver.insert(People.CONTENT_URI, values);
795        long personId = ContentUris.parseId(personUri);
796
797        People.addToMyContactsGroup(mResolver, personId);
798
799        if (photo) {
800            values.clear();
801            byte[] photoData = loadTestPhoto();
802            values.put(Photos.DATA, photoData);
803            values.put(Photos.LOCAL_VERSION, "1");
804            values.put(Photos.EXISTS_ON_SERVER, 0);
805            Uri photoUri = Uri.withAppendedPath(personUri, Photos.CONTENT_DIRECTORY);
806            mResolver.update(photoUri, values, null, null);
807        }
808
809        if (organization) {
810            values.clear();
811            values.put(Organizations.ISPRIMARY, 1);
812            values.put(Organizations.COMPANY, "Google");
813            values.put(Organizations.TYPE, Organizations.TYPE_WORK);
814            values.put(Organizations.PERSON_ID, personId);
815            mResolver.insert(Organizations.CONTENT_URI, values);
816        }
817
818        if (email) {
819            values.clear();
820            values.put(ContactMethods.PERSON_ID, personId);
821            values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
822            values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
823            values.put(ContactMethods.DATA, "foo@acme.com");
824            values.put(ContactMethods.ISPRIMARY, 1);
825            mResolver.insert(ContactMethods.CONTENT_URI, values);
826
827
828            String protocol = ContactMethods
829                    .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
830            values.clear();
831            values.put(Presence.IM_PROTOCOL, protocol);
832            values.put(Presence.IM_HANDLE, "foo@acme.com");
833            values.put(Presence.IM_ACCOUNT, "foo");
834            values.put(Presence.PRESENCE_STATUS, Presence.OFFLINE);
835            values.put(Presence.PRESENCE_CUSTOM_STATUS, "Coding for Android");
836            mResolver.insert(Presence.CONTENT_URI, values);
837        }
838
839        if (phone) {
840            values.clear();
841            values.put(Phones.PERSON_ID, personId);
842            values.put(Phones.TYPE, Phones.TYPE_HOME);
843            values.put(Phones.NUMBER, "1-800-4664-411");
844            values.put(Phones.ISPRIMARY, 1);
845            mResolver.insert(Phones.CONTENT_URI, values);
846        }
847
848        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
849                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath(query).build();
850
851        Cursor c = mResolver.query(searchUri, null, null, null, null);
852        assertEquals(1, c.getCount());
853        c.moveToFirst();
854        values.clear();
855
856        String icon1 = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1));
857        if (expectIcon1Uri) {
858            assertTrue(icon1.startsWith("content:"));
859        } else {
860            assertEquals(String.valueOf(com.android.internal.R.drawable.ic_contact_picture), icon1);
861        }
862
863        // SearchManager does not declare a constant for _id
864        values.put("_id", personId);
865        values.put(SearchManager.SUGGEST_COLUMN_ICON_2, expectedIcon2);
866        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, personId);
867        values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, personId);
868        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, expectedText1);
869        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, expectedText2);
870        assertCursorValues(c, values);
871        c.close();
872
873        // Cleanup
874        mResolver.delete(personUri, null, null);
875    }
876
877    /**
878     * Capturing the search suggestion requirements in test cases as a reference.
879     */
880    public void testSearchSuggestionsByPhoneNumber() throws Exception {
881
882        // We don't provide compatibility for search suggestions
883        if (!USE_LEGACY_PROVIDER) {
884            return;
885        }
886
887        ContentValues values = new ContentValues();
888
889        Uri searchUri = new Uri.Builder().scheme("content").authority(Contacts.AUTHORITY)
890                .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath("12345").build();
891
892        Cursor c = mResolver.query(searchUri, null, null, null, null);
893        assertEquals(2, c.getCount());
894        c.moveToFirst();
895
896        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Execute");
897        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "");
898        values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
899                String.valueOf(com.android.internal.R.drawable.call_contact));
900        values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
901                Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
902        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345");
903        values.putNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
904        assertCursorValues(c, values);
905
906        c.moveToNext();
907        values.clear();
908        values.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "Dial number");
909        values.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "using 12345");
910        values.put(SearchManager.SUGGEST_COLUMN_ICON_1,
911                String.valueOf(com.android.internal.R.drawable.create_contact));
912        values.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
913                Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
914        values.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, "tel:12345");
915        values.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
916                SearchManager.SUGGEST_NEVER_MAKE_SHORTCUT);
917        assertCursorValues(c, values);
918        c.close();
919    }
920
921    private Uri insertPerson(String name, ContentValues values) {
922        values.put(People.NAME, name);
923        return mResolver.insert(People.CONTENT_URI, values);
924    }
925
926    private Uri insertLegacyGroup(String name, ContentValues values) {
927        values.put(Groups.NAME, name);
928        return mResolver.insert(Groups.CONTENT_URI, values);
929    }
930
931    private Uri insertLegacyGroupMembership(Uri groupUri, Uri personUri, ContentValues values) {
932        long groupId = ContentUris.parseId(groupUri);
933        long personId = ContentUris.parseId(personUri);
934
935        values.clear();
936        values.put(GroupMembership.GROUP_ID, groupId);
937        values.put(GroupMembership.PERSON_ID, personId);
938        Uri membershipUri = mResolver.insert(GroupMembership.CONTENT_URI, values);
939        return membershipUri;
940    }
941
942    private void putContactValues(ContentValues values) {
943        putContactValuesExceptName(values);
944        values.put(People.NAME, "Deer Dough");
945    }
946
947    private void putContactValuesExceptName(ContentValues values) {
948        // Populating only unhidden columns
949        values.put(People.PHONETIC_NAME, "Dear Doe");
950        values.put(People.NOTES, "Cash Cow");
951        values.put(People.TIMES_CONTACTED, 3);
952        values.put(People.LAST_TIME_CONTACTED, 10);
953        values.put(People.CUSTOM_RINGTONE, "ringtone");
954        values.put(People.SEND_TO_VOICEMAIL, 1);
955        values.put(People.STARRED, 1);
956    }
957
958    private void putContactValues2(ContentValues values) {
959        // Populating only unhidden columns
960        values.put(People.NAME, "John Doe");
961        values.put(People.PHONETIC_NAME, "jon dawe");
962        values.put(People.NOTES, "Poor Johnny");
963        values.put(People.TIMES_CONTACTED, 4);
964        values.put(People.LAST_TIME_CONTACTED, 11);
965        values.put(People.CUSTOM_RINGTONE, "rangtone");
966        values.put(People.SEND_TO_VOICEMAIL, 0);
967        values.put(People.STARRED, 0);
968    }
969
970    private void assertFilteredContacts(String filter, String... expectedNames) {
971        Uri filterUri = Uri.withAppendedPath(People.CONTENT_FILTER_URI, filter);
972        Cursor c = mResolver.query(filterUri, null, null, null, null);
973        try {
974            assertEquals("Record count", expectedNames.length, c.getCount());
975            int column = c.getColumnIndex(People.NAME);
976            for (int i = 0; i < expectedNames.length; i++) {
977                c.moveToNext();
978                assertEquals(expectedNames[i], c.getString(column));
979            }
980        } finally {
981            c.close();
982        }
983    }
984
985    private void assertPersonIdConstraint(Uri uri, String typeColumn, int defaultType) {
986        ContentValues values = new ContentValues();
987        values.put(typeColumn, defaultType);
988        try {
989            mResolver.insert(uri, values);
990            fail("Inserted row without person ID");
991        } catch (Exception e) {
992            // Exception expected
993        }
994    }
995
996    private void assertTypeAndLabelConstraints(Uri uri, String personIdColumn, long personId,
997            String typeColumn, int defaultType, int otherType, String labelColumn) {
998        ContentValues values = new ContentValues();
999        values.put(personIdColumn, personId);
1000        values.put(labelColumn, "Foo");
1001
1002        try {
1003            mResolver.insert(uri, values);
1004            fail("Inserted row with label without defining type");
1005        } catch (Exception e) {
1006            // Exception expected
1007        }
1008    }
1009
1010    protected void assertSelection(Uri uri, ContentValues values, String legacyTable,
1011            String idColumn, long id) {
1012        values.put(idColumn, id);
1013        String qualified = legacyTable + "." + idColumn;
1014        super.assertSelection(uri, values, qualified, id);
1015    }
1016}
1017