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