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