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