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