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.ContactsActor.PACKAGE_GREY;
20 import static com.android.providers.contacts.TestUtils.cv;
21 import static com.android.providers.contacts.TestUtils.dumpCursor;
22 
23 import android.accounts.Account;
24 import android.content.ContentProvider;
25 import android.content.ContentResolver;
26 import android.content.ContentUris;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.Entity;
30 import android.database.Cursor;
31 import android.database.sqlite.SQLiteDatabase;
32 import android.net.Uri;
33 import android.provider.BaseColumns;
34 import android.provider.CallLog;
35 import android.provider.CallLog.Calls;
36 import android.provider.ContactsContract;
37 import android.provider.ContactsContract.AggregationExceptions;
38 import android.provider.ContactsContract.CommonDataKinds.Email;
39 import android.provider.ContactsContract.CommonDataKinds.Event;
40 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
41 import android.provider.ContactsContract.CommonDataKinds.Identity;
42 import android.provider.ContactsContract.CommonDataKinds.Im;
43 import android.provider.ContactsContract.CommonDataKinds.Nickname;
44 import android.provider.ContactsContract.CommonDataKinds.Note;
45 import android.provider.ContactsContract.CommonDataKinds.Organization;
46 import android.provider.ContactsContract.CommonDataKinds.Phone;
47 import android.provider.ContactsContract.CommonDataKinds.Photo;
48 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
49 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
50 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
51 import android.provider.ContactsContract.Contacts;
52 import android.provider.ContactsContract.Data;
53 import android.provider.ContactsContract.Groups;
54 import android.provider.ContactsContract.RawContacts;
55 import android.provider.ContactsContract.Settings;
56 import android.provider.ContactsContract.StatusUpdates;
57 import android.provider.ContactsContract.StreamItems;
58 import android.provider.VoicemailContract;
59 import android.test.MoreAsserts;
60 import android.test.mock.MockContentResolver;
61 import android.util.Log;
62 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
63 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
64 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
65 import com.android.providers.contacts.testutil.DataUtil;
66 import com.android.providers.contacts.testutil.RawContactUtil;
67 import com.android.providers.contacts.testutil.TestUtil;
68 import com.android.providers.contacts.util.Hex;
69 import com.android.providers.contacts.util.MockClock;
70 import com.google.android.collect.Sets;
71 
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.BitSet;
75 import java.util.Comparator;
76 import java.util.HashSet;
77 import java.util.Iterator;
78 import java.util.Map;
79 import java.util.Map.Entry;
80 import java.util.Set;
81 
82 /**
83  * A common superclass for {@link ContactsProvider2}-related tests.
84  */
85 public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase {
86 
87     static final String ADD_VOICEMAIL_PERMISSION =
88             "com.android.voicemail.permission.ADD_VOICEMAIL";
89     /*
90      * Permission to allow querying voicemails
91      */
92     static final String READ_VOICEMAIL_PERMISSION =
93             "com.android.voicemail.permission.READ_VOICEMAIL";
94     /*
95      * Permission to allow deleting and updating voicemails
96      */
97     static final String WRITE_VOICEMAIL_PERMISSION =
98             "com.android.voicemail.permission.WRITE_VOICEMAIL";
99 
100     protected static final String PACKAGE = "ContactsProvider2Test";
101     public static final String READ_ONLY_ACCOUNT_TYPE =
102             SynchronousContactsProvider2.READ_ONLY_ACCOUNT_TYPE;
103 
104     protected ContactsActor mActor;
105     protected MockContentResolver mResolver;
106     protected Account mAccount = new Account("account1", "account type1");
107     protected Account mAccountTwo = new Account("account2", "account type2");
108 
109     protected final static Long NO_LONG = new Long(0);
110     protected final static String NO_STRING = new String("");
111     protected final static Account NO_ACCOUNT = new Account("a", "b");
112 
113     /**
114      * Use {@link MockClock#install()} to start using it.
115      * It'll be automatically uninstalled by {@link #tearDown()}.
116      */
117     protected static final MockClock sMockClock = new MockClock();
118 
getProviderClass()119     protected Class<? extends ContentProvider> getProviderClass() {
120         return SynchronousContactsProvider2.class;
121     }
122 
getAuthority()123     protected String getAuthority() {
124         return ContactsContract.AUTHORITY;
125     }
126 
127     @Override
setUp()128     protected void setUp() throws Exception {
129         super.setUp();
130 
131         mActor = new ContactsActor(
132                 getContext(), getContextPackageName(), getProviderClass(), getAuthority());
133         mResolver = mActor.resolver;
134         if (mActor.provider instanceof SynchronousContactsProvider2) {
135             getContactsProvider().wipeData();
136         }
137 
138         // Give the actor access to read/write contacts and profile data by default.
139         mActor.addPermissions(
140                 "android.permission.READ_CONTACTS",
141                 "android.permission.WRITE_CONTACTS",
142                 "android.permission.READ_WRITE_CONTACT_METADATA",
143                 "android.permission.READ_SOCIAL_STREAM",
144                 "android.permission.WRITE_SOCIAL_STREAM");
145     }
146 
getContextPackageName()147     protected String getContextPackageName() {
148         return PACKAGE_GREY;
149     }
150 
151     @Override
tearDown()152     protected void tearDown() throws Exception {
153         mActor.shutdown();
154         sMockClock.uninstall();
155         super.tearDown();
156     }
157 
getContactsProvider()158     public SynchronousContactsProvider2 getContactsProvider() {
159         return (SynchronousContactsProvider2) mActor.provider;
160     }
161 
getMockContext()162     public Context getMockContext() {
163         return mActor.context;
164     }
165 
addProvider(Class<T> providerClass, String authority)166     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
167             String authority) throws Exception {
168         return mActor.addProvider(providerClass, authority);
169     }
170 
getProvider()171     public ContentProvider getProvider() {
172         return mActor.provider;
173     }
174 
setCallerIsSyncAdapter(Uri uri, Account account)175     protected Uri setCallerIsSyncAdapter(Uri uri, Account account) {
176         if (account == null) {
177             return uri;
178         }
179         final Uri.Builder builder = uri.buildUpon();
180         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
181         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
182         builder.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true");
183         return builder.build();
184     }
185 
updateItem(Uri uri, long id, String... extras)186     protected int updateItem(Uri uri, long id, String... extras) {
187         Uri itemUri = ContentUris.withAppendedId(uri, id);
188         return updateItem(itemUri, extras);
189     }
190 
updateItem(Uri uri, String... extras)191     protected int updateItem(Uri uri, String... extras) {
192         ContentValues values = new ContentValues();
193         CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
194         return mResolver.update(uri, values, null, null);
195     }
196 
createGroup(Account account, String sourceId, String title)197     protected long createGroup(Account account, String sourceId, String title) {
198         return createGroup(account, sourceId, title, 1, false, false);
199     }
200 
createGroup(Account account, String sourceId, String title, int visible)201     protected long createGroup(Account account, String sourceId, String title, int visible) {
202         return createGroup(account, sourceId, title, visible, false, false);
203     }
204 
createAutoAddGroup(Account account)205     protected long createAutoAddGroup(Account account) {
206         return createGroup(account, "auto", "auto",
207                 0 /* visible */,  true /* auto-add */, false /* fav */);
208     }
209 
createGroup(Account account, String sourceId, String title, int visible, boolean autoAdd, boolean favorite)210     protected long createGroup(Account account, String sourceId, String title,
211             int visible, boolean autoAdd, boolean favorite) {
212         ContentValues values = new ContentValues();
213         values.put(Groups.SOURCE_ID, sourceId);
214         values.put(Groups.TITLE, title);
215         values.put(Groups.GROUP_VISIBLE, visible);
216         values.put(Groups.AUTO_ADD, autoAdd ? 1 : 0);
217         values.put(Groups.FAVORITES, favorite ? 1 : 0);
218         final Uri uri = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account);
219         return ContentUris.parseId(mResolver.insert(uri, values));
220     }
221 
createSettings(Account account, String shouldSync, String ungroupedVisible)222     protected void createSettings(Account account, String shouldSync, String ungroupedVisible) {
223         createSettings(new AccountWithDataSet(account.name, account.type, null),
224                 shouldSync, ungroupedVisible);
225     }
226 
createSettings(AccountWithDataSet account, String shouldSync, String ungroupedVisible)227     protected void createSettings(AccountWithDataSet account, String shouldSync,
228             String ungroupedVisible) {
229         ContentValues values = new ContentValues();
230         values.put(Settings.ACCOUNT_NAME, account.getAccountName());
231         values.put(Settings.ACCOUNT_TYPE, account.getAccountType());
232         if (account.getDataSet() != null) {
233             values.put(Settings.DATA_SET, account.getDataSet());
234         }
235         values.put(Settings.SHOULD_SYNC, shouldSync);
236         values.put(Settings.UNGROUPED_VISIBLE, ungroupedVisible);
237         mResolver.insert(Settings.CONTENT_URI, values);
238     }
239 
insertOrganization(long rawContactId, ContentValues values)240     protected Uri insertOrganization(long rawContactId, ContentValues values) {
241         return insertOrganization(rawContactId, values, false, false);
242     }
243 
insertOrganization(long rawContactId, ContentValues values, boolean primary)244     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary) {
245         return insertOrganization(rawContactId, values, primary, false);
246     }
247 
insertOrganization(long rawContactId, ContentValues values, boolean primary, boolean superPrimary)248     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary,
249             boolean superPrimary) {
250         values.put(Data.RAW_CONTACT_ID, rawContactId);
251         values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
252         values.put(Organization.TYPE, Organization.TYPE_WORK);
253         if (primary) {
254             values.put(Data.IS_PRIMARY, 1);
255         }
256         if (superPrimary) {
257             values.put(Data.IS_SUPER_PRIMARY, 1);
258         }
259 
260         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
261         return resultUri;
262     }
263 
insertPhoneNumber(long rawContactId, String phoneNumber)264     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber) {
265         return insertPhoneNumber(rawContactId, phoneNumber, false);
266     }
267 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary)268     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary) {
269         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, Phone.TYPE_HOME);
270     }
271 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, boolean superPrimary)272     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
273             boolean superPrimary) {
274         return insertPhoneNumber(rawContactId, phoneNumber, primary, superPrimary, Phone.TYPE_HOME);
275     }
276 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, int type)277     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
278             int type) {
279         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, type);
280     }
281 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, boolean superPrimary, int type)282     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
283             boolean superPrimary, int type) {
284         ContentValues values = new ContentValues();
285         values.put(Data.RAW_CONTACT_ID, rawContactId);
286         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
287         values.put(Phone.NUMBER, phoneNumber);
288         values.put(Phone.TYPE, type);
289         if (primary) {
290             values.put(Data.IS_PRIMARY, 1);
291         }
292         if (superPrimary) {
293             values.put(Data.IS_SUPER_PRIMARY, 1);
294         }
295 
296         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
297         return resultUri;
298     }
299 
insertEmail(long rawContactId, String email)300     protected Uri insertEmail(long rawContactId, String email) {
301         return insertEmail(rawContactId, email, false);
302     }
303 
insertEmail(long rawContactId, String email, boolean primary)304     protected Uri insertEmail(long rawContactId, String email, boolean primary) {
305         return insertEmail(rawContactId, email, primary, Email.TYPE_HOME, null);
306     }
307 
insertEmail(long rawContactId, String email, boolean primary, boolean superPrimary)308     protected Uri insertEmail(long rawContactId, String email, boolean primary,
309             boolean superPrimary) {
310         return insertEmail(rawContactId, email, primary, superPrimary, Email.TYPE_HOME, null);
311     }
312 
insertEmail(long rawContactId, String email, boolean primary, int type, String label)313     protected Uri insertEmail(long rawContactId, String email, boolean primary, int type,
314             String label) {
315         return insertEmail(rawContactId, email, primary, false, type, label);
316     }
317 
insertEmail(long rawContactId, String email, boolean primary, boolean superPrimary, int type, String label)318     protected Uri insertEmail(long rawContactId, String email, boolean primary,
319             boolean superPrimary, int type,  String label) {
320         ContentValues values = new ContentValues();
321         values.put(Data.RAW_CONTACT_ID, rawContactId);
322         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
323         values.put(Email.DATA, email);
324         values.put(Email.TYPE, type);
325         values.put(Email.LABEL, label);
326         if (primary) {
327             values.put(Data.IS_PRIMARY, 1);
328         }
329         if (superPrimary) {
330             values.put(Data.IS_SUPER_PRIMARY, 1);
331         }
332 
333         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
334         return resultUri;
335     }
336 
insertSipAddress(long rawContactId, String sipAddress)337     protected Uri insertSipAddress(long rawContactId, String sipAddress) {
338         return insertSipAddress(rawContactId, sipAddress, false);
339     }
340 
insertSipAddress(long rawContactId, String sipAddress, boolean primary)341     protected Uri insertSipAddress(long rawContactId, String sipAddress, boolean primary) {
342         ContentValues values = new ContentValues();
343         values.put(Data.RAW_CONTACT_ID, rawContactId);
344         values.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
345         values.put(SipAddress.SIP_ADDRESS, sipAddress);
346         if (primary) {
347             values.put(Data.IS_PRIMARY, 1);
348         }
349 
350         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
351         return resultUri;
352     }
353 
insertNickname(long rawContactId, String nickname)354     protected Uri insertNickname(long rawContactId, String nickname) {
355         ContentValues values = new ContentValues();
356         values.put(Data.RAW_CONTACT_ID, rawContactId);
357         values.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
358         values.put(Nickname.NAME, nickname);
359         values.put(Nickname.TYPE, Nickname.TYPE_OTHER_NAME);
360 
361         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
362         return resultUri;
363     }
364 
insertPostalAddress(long rawContactId, String formattedAddress)365     protected Uri insertPostalAddress(long rawContactId, String formattedAddress) {
366         ContentValues values = new ContentValues();
367         values.put(Data.RAW_CONTACT_ID, rawContactId);
368         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
369         values.put(StructuredPostal.FORMATTED_ADDRESS, formattedAddress);
370 
371         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
372         return resultUri;
373     }
374 
insertPostalAddress(long rawContactId, ContentValues values)375     protected Uri insertPostalAddress(long rawContactId, ContentValues values) {
376         values.put(Data.RAW_CONTACT_ID, rawContactId);
377         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
378         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
379         return resultUri;
380     }
381 
insertPhoto(long rawContactId)382     protected Uri insertPhoto(long rawContactId) {
383         ContentValues values = new ContentValues();
384         values.put(Data.RAW_CONTACT_ID, rawContactId);
385         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
386         values.put(Photo.PHOTO, loadTestPhoto());
387         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
388         return resultUri;
389     }
390 
insertPhoto(long rawContactId, int resourceId)391     protected Uri insertPhoto(long rawContactId, int resourceId) {
392         ContentValues values = new ContentValues();
393         values.put(Data.RAW_CONTACT_ID, rawContactId);
394         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
395         values.put(Photo.PHOTO, loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL));
396         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
397         return resultUri;
398     }
399 
insertGroupMembership(long rawContactId, String sourceId)400     protected Uri insertGroupMembership(long rawContactId, String sourceId) {
401         ContentValues values = new ContentValues();
402         values.put(Data.RAW_CONTACT_ID, rawContactId);
403         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
404         values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
405         return mResolver.insert(Data.CONTENT_URI, values);
406     }
407 
insertGroupMembership(long rawContactId, Long groupId)408     protected Uri insertGroupMembership(long rawContactId, Long groupId) {
409         ContentValues values = new ContentValues();
410         values.put(Data.RAW_CONTACT_ID, rawContactId);
411         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
412         values.put(GroupMembership.GROUP_ROW_ID, groupId);
413         return mResolver.insert(Data.CONTENT_URI, values);
414     }
415 
removeGroupMemberships(long rawContactId)416     public void removeGroupMemberships(long rawContactId) {
417         mResolver.delete(Data.CONTENT_URI,
418                 Data.MIMETYPE + "=? AND " + GroupMembership.RAW_CONTACT_ID + "=?",
419                 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(rawContactId) });
420     }
421 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, int chatMode)422     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
423             int presence, String status, int chatMode) {
424         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, chatMode,
425                 false);
426     }
427 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, int chatMode, boolean isUserProfile)428     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
429             int presence, String status, int chatMode, boolean isUserProfile) {
430         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, 0, chatMode,
431                 isUserProfile);
432     }
433 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)434     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
435             int presence, String status, long timestamp, int chatMode, boolean isUserProfile) {
436         ContentValues values = new ContentValues();
437         values.put(StatusUpdates.PROTOCOL, protocol);
438         values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
439         values.put(StatusUpdates.IM_HANDLE, handle);
440         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
441     }
442 
insertStatusUpdate( long dataId, int presence, String status, long timestamp, int chatMode)443     protected Uri insertStatusUpdate(
444             long dataId, int presence, String status, long timestamp, int chatMode) {
445         return insertStatusUpdate(dataId, presence, status, timestamp, chatMode, false);
446     }
447 
insertStatusUpdate( long dataId, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)448     protected Uri insertStatusUpdate(
449             long dataId, int presence, String status, long timestamp, int chatMode,
450             boolean isUserProfile) {
451         ContentValues values = new ContentValues();
452         values.put(StatusUpdates.DATA_ID, dataId);
453         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
454     }
455 
insertStatusUpdate( ContentValues values, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)456     private Uri insertStatusUpdate(
457             ContentValues values, int presence, String status, long timestamp, int chatMode,
458             boolean isUserProfile) {
459         if (presence != 0) {
460             values.put(StatusUpdates.PRESENCE, presence);
461             values.put(StatusUpdates.CHAT_CAPABILITY, chatMode);
462         }
463         if (status != null) {
464             values.put(StatusUpdates.STATUS, status);
465         }
466         if (timestamp != 0) {
467             values.put(StatusUpdates.STATUS_TIMESTAMP, timestamp);
468         }
469 
470         Uri insertUri = isUserProfile
471                 ? StatusUpdates.PROFILE_CONTENT_URI
472                 : StatusUpdates.CONTENT_URI;
473         Uri resultUri = mResolver.insert(insertUri, values);
474         return resultUri;
475     }
476 
insertStreamItem(long rawContactId, ContentValues values, Account account)477     protected Uri insertStreamItem(long rawContactId, ContentValues values, Account account) {
478         return mResolver.insert(
479                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
480                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
481                         RawContacts.StreamItems.CONTENT_DIRECTORY), account),
482                 values);
483     }
484 
insertStreamItemPhoto(long streamItemId, ContentValues values, Account account)485     protected Uri insertStreamItemPhoto(long streamItemId, ContentValues values, Account account) {
486         return mResolver.insert(
487                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
488                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
489                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), account),
490                 values);
491     }
492 
insertImHandle(long rawContactId, int protocol, String customProtocol, String handle)493     protected Uri insertImHandle(long rawContactId, int protocol, String customProtocol,
494             String handle) {
495         ContentValues values = new ContentValues();
496         values.put(Data.RAW_CONTACT_ID, rawContactId);
497         values.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
498         values.put(Im.PROTOCOL, protocol);
499         values.put(Im.CUSTOM_PROTOCOL, customProtocol);
500         values.put(Im.DATA, handle);
501         values.put(Im.TYPE, Im.TYPE_HOME);
502 
503         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
504         return resultUri;
505     }
506 
insertEvent(long rawContactId, int type, String date)507     protected Uri insertEvent(long rawContactId, int type, String date) {
508         ContentValues values = new ContentValues();
509         values.put(Data.RAW_CONTACT_ID, rawContactId);
510         values.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
511         values.put(Event.TYPE, type);
512         values.put(Event.START_DATE, date);
513         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
514         return resultUri;
515     }
516 
insertNote(long rawContactId, String note)517     protected Uri insertNote(long rawContactId, String note) {
518         ContentValues values = new ContentValues();
519         values.put(Data.RAW_CONTACT_ID, rawContactId);
520         values.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
521         values.put(Note.NOTE, note);
522         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
523         return resultUri;
524     }
525 
insertIdentity(long rawContactId, String identity, String namespace)526     protected Uri insertIdentity(long rawContactId, String identity, String namespace) {
527         ContentValues values = new ContentValues();
528         values.put(Data.RAW_CONTACT_ID, rawContactId);
529         values.put(Data.MIMETYPE, Identity.CONTENT_ITEM_TYPE);
530         values.put(Identity.NAMESPACE, namespace);
531         values.put(Identity.IDENTITY, identity);
532 
533         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
534         return resultUri;
535     }
536 
setContactAccount(long rawContactId, String accountType, String accountName)537     protected void setContactAccount(long rawContactId, String accountType, String accountName) {
538         ContentValues values = new ContentValues();
539         values.put(RawContacts.ACCOUNT_TYPE, accountType);
540         values.put(RawContacts.ACCOUNT_NAME, accountName);
541 
542         mResolver.update(ContentUris.withAppendedId(
543                 RawContacts.CONTENT_URI, rawContactId), values, null, null);
544     }
545 
setAggregationException(int type, long rawContactId1, long rawContactId2)546     protected void setAggregationException(int type, long rawContactId1, long rawContactId2) {
547         ContentValues values = new ContentValues();
548         values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
549         values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
550         values.put(AggregationExceptions.TYPE, type);
551         assertEquals(1, mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null));
552     }
553 
setRawContactCustomization(long rawContactId, int starred, int sendToVoiceMail)554     protected void setRawContactCustomization(long rawContactId, int starred, int sendToVoiceMail) {
555         ContentValues values = new ContentValues();
556 
557         values.put(RawContacts.STARRED, starred);
558         values.put(RawContacts.SEND_TO_VOICEMAIL, sendToVoiceMail);
559 
560         assertEquals(1, mResolver.update(ContentUris.withAppendedId(
561                 RawContacts.CONTENT_URI, rawContactId), values, null, null));
562     }
563 
markInvisible(long contactId)564     protected void markInvisible(long contactId) {
565         // There's no api for this, so we just tweak the DB directly.
566         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
567                 .getWritableDatabase();
568         db.execSQL("DELETE FROM " + Tables.DEFAULT_DIRECTORY +
569                 " WHERE " + BaseColumns._ID + "=" + contactId);
570     }
571 
createAccount(String accountName, String accountType, String dataSet)572     protected long createAccount(String accountName, String accountType, String dataSet) {
573         // There's no api for this, so we just tweak the DB directly.
574         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
575                 .getWritableDatabase();
576 
577         ContentValues values = new ContentValues();
578         values.put(AccountsColumns.ACCOUNT_NAME, accountName);
579         values.put(AccountsColumns.ACCOUNT_TYPE, accountType);
580         values.put(AccountsColumns.DATA_SET, dataSet);
581         return db.insert(Tables.ACCOUNTS, null, values);
582     }
583 
queryRawContact(long rawContactId)584     protected Cursor queryRawContact(long rawContactId) {
585         return mResolver.query(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
586                 null, null, null, null);
587     }
588 
queryContact(long contactId)589     protected Cursor queryContact(long contactId) {
590         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
591                 null, null, null, null);
592     }
593 
queryContact(long contactId, String[] projection)594     protected Cursor queryContact(long contactId, String[] projection) {
595         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
596                 projection, null, null, null);
597     }
598 
getContactUriForRawContact(long rawContactId)599     protected Uri getContactUriForRawContact(long rawContactId) {
600         return ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId));
601     }
602 
queryContactId(long rawContactId)603     protected long queryContactId(long rawContactId) {
604         Cursor c = queryRawContact(rawContactId);
605         assertTrue(c.moveToFirst());
606         long contactId = c.getLong(c.getColumnIndex(RawContacts.CONTACT_ID));
607         c.close();
608         return contactId;
609     }
610 
queryPhotoId(long contactId)611     protected long queryPhotoId(long contactId) {
612         Cursor c = queryContact(contactId);
613         assertTrue(c.moveToFirst());
614         long photoId = c.getInt(c.getColumnIndex(Contacts.PHOTO_ID));
615         c.close();
616         return photoId;
617     }
618 
queryPhotoFileId(long contactId)619     protected long queryPhotoFileId(long contactId) {
620         return getStoredLongValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
621                 Contacts.PHOTO_FILE_ID);
622     }
623 
queryRawContactIsStarred(long rawContactId)624     protected boolean queryRawContactIsStarred(long rawContactId) {
625         Cursor c = queryRawContact(rawContactId);
626         try {
627             assertTrue(c.moveToFirst());
628             return c.getLong(c.getColumnIndex(RawContacts.STARRED)) != 0;
629         } finally {
630             c.close();
631         }
632     }
633 
queryDisplayName(long contactId)634     protected String queryDisplayName(long contactId) {
635         Cursor c = queryContact(contactId);
636         assertTrue(c.moveToFirst());
637         String displayName = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
638         c.close();
639         return displayName;
640     }
641 
queryLookupKey(long contactId)642     protected String queryLookupKey(long contactId) {
643         Cursor c = queryContact(contactId);
644         assertTrue(c.moveToFirst());
645         String lookupKey = c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY));
646         c.close();
647         return lookupKey;
648     }
649 
assertAggregated(long rawContactId1, long rawContactId2)650     protected void assertAggregated(long rawContactId1, long rawContactId2) {
651         long contactId1 = queryContactId(rawContactId1);
652         long contactId2 = queryContactId(rawContactId2);
653         assertTrue(contactId1 == contactId2);
654     }
655 
assertAggregated(long rawContactId1, long rawContactId2, String expectedDisplayName)656     protected void assertAggregated(long rawContactId1, long rawContactId2,
657             String expectedDisplayName) {
658         long contactId1 = queryContactId(rawContactId1);
659         long contactId2 = queryContactId(rawContactId2);
660         assertTrue(contactId1 == contactId2);
661 
662         String displayName = queryDisplayName(contactId1);
663         assertEquals(expectedDisplayName, displayName);
664     }
665 
assertNotAggregated(long rawContactId1, long rawContactId2)666     protected void assertNotAggregated(long rawContactId1, long rawContactId2) {
667         long contactId1 = queryContactId(rawContactId1);
668         long contactId2 = queryContactId(rawContactId2);
669         assertTrue(contactId1 != contactId2);
670     }
671 
assertStructuredName(long rawContactId, String prefix, String givenName, String middleName, String familyName, String suffix)672     protected void assertStructuredName(long rawContactId, String prefix, String givenName,
673             String middleName, String familyName, String suffix) {
674         Uri uri = Uri.withAppendedPath(
675                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
676                 RawContacts.Data.CONTENT_DIRECTORY);
677 
678         final String[] projection = new String[] {
679                 StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
680                 StructuredName.FAMILY_NAME, StructuredName.SUFFIX
681         };
682 
683         Cursor c = mResolver.query(uri, projection, Data.MIMETYPE + "='"
684                 + StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
685 
686         assertTrue(c.moveToFirst());
687         assertEquals(prefix, c.getString(0));
688         assertEquals(givenName, c.getString(1));
689         assertEquals(middleName, c.getString(2));
690         assertEquals(familyName, c.getString(3));
691         assertEquals(suffix, c.getString(4));
692         c.close();
693     }
694 
assertSingleGroup(Long rowId, Account account, String sourceId, String title)695     protected long assertSingleGroup(Long rowId, Account account, String sourceId, String title) {
696         Cursor c = mResolver.query(Groups.CONTENT_URI, null, null, null, null);
697         try {
698             assertTrue(c.moveToNext());
699             long actualRowId = assertGroup(c, rowId, account, sourceId, title);
700             assertFalse(c.moveToNext());
701             return actualRowId;
702         } finally {
703             c.close();
704         }
705     }
706 
assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId, String sourceId)707     protected long assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId,
708             String sourceId) {
709         Cursor c = mResolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
710         try {
711             assertTrue(c.moveToNext());
712             long actualRowId = assertGroupMembership(c, rowId, rawContactId, groupRowId, sourceId);
713             assertFalse(c.moveToNext());
714             return actualRowId;
715         } finally {
716             c.close();
717         }
718     }
719 
assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId, String sourceId)720     protected long assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId,
721             String sourceId) {
722         assertNullOrEquals(c, rowId, Data._ID);
723         assertNullOrEquals(c, rawContactId, GroupMembership.RAW_CONTACT_ID);
724         assertNullOrEquals(c, groupRowId, GroupMembership.GROUP_ROW_ID);
725         assertNullOrEquals(c, sourceId, GroupMembership.GROUP_SOURCE_ID);
726         return c.getLong(c.getColumnIndexOrThrow("_id"));
727     }
728 
assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title)729     protected long assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title) {
730         assertNullOrEquals(c, rowId, Groups._ID);
731         assertNullOrEquals(c, account);
732         assertNullOrEquals(c, sourceId, Groups.SOURCE_ID);
733         assertNullOrEquals(c, title, Groups.TITLE);
734         return c.getLong(c.getColumnIndexOrThrow("_id"));
735     }
736 
assertNullOrEquals(Cursor c, Account account)737     private void assertNullOrEquals(Cursor c, Account account) {
738         if (account == NO_ACCOUNT) {
739             return;
740         }
741         if (account == null) {
742             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
743             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
744         } else {
745             assertEquals(account.name, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
746             assertEquals(account.type, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
747         }
748     }
749 
assertNullOrEquals(Cursor c, Long value, String columnName)750     private void assertNullOrEquals(Cursor c, Long value, String columnName) {
751         if (value != NO_LONG) {
752             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
753             else assertEquals((long) value, c.getLong(c.getColumnIndexOrThrow(columnName)));
754         }
755     }
756 
assertNullOrEquals(Cursor c, String value, String columnName)757     private void assertNullOrEquals(Cursor c, String value, String columnName) {
758         if (value != NO_STRING) {
759             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
760             else assertEquals(value, c.getString(c.getColumnIndexOrThrow(columnName)));
761         }
762     }
763 
assertSuperPrimary(Long dataId, boolean isSuperPrimary)764     protected void assertSuperPrimary(Long dataId, boolean isSuperPrimary) {
765         final String[] projection = new String[]{Data.MIMETYPE, Data._ID, Data.IS_SUPER_PRIMARY};
766         Cursor c = mResolver.query(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
767                 projection, null, null, null);
768 
769         c.moveToFirst();
770         if (isSuperPrimary) {
771             assertEquals(1, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
772         } else {
773             assertEquals(0, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
774         }
775 
776     }
777 
assertDataRow(ContentValues actual, String expectedMimetype, Object... expectedArguments)778     protected void assertDataRow(ContentValues actual, String expectedMimetype,
779             Object... expectedArguments) {
780         assertEquals(actual.toString(), expectedMimetype, actual.getAsString(Data.MIMETYPE));
781         for (int i = 0; i < expectedArguments.length; i += 2) {
782             String columnName = (String) expectedArguments[i];
783             Object expectedValue = expectedArguments[i + 1];
784             if (expectedValue instanceof Uri) {
785                 expectedValue = ContentUris.parseId((Uri) expectedValue);
786             }
787             if (expectedValue == null) {
788                 assertNull(actual.toString(), actual.get(columnName));
789             }
790             if (expectedValue instanceof Long) {
791                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
792                         expectedValue, actual.getAsLong(columnName));
793             } else if (expectedValue instanceof Integer) {
794                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
795                         expectedValue, actual.getAsInteger(columnName));
796             } else if (expectedValue instanceof String) {
797                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
798                         expectedValue, actual.getAsString(columnName));
799             } else {
800                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
801                         expectedValue, actual.get(columnName));
802             }
803         }
804     }
805 
assertNoRowsAndClose(Cursor c)806     protected void assertNoRowsAndClose(Cursor c) {
807         try {
808             assertFalse(c.moveToNext());
809         } finally {
810             c.close();
811         }
812     }
813 
814     protected static class IdComparator implements Comparator<ContentValues> {
815         @Override
compare(ContentValues o1, ContentValues o2)816         public int compare(ContentValues o1, ContentValues o2) {
817             long id1 = o1.getAsLong(ContactsContract.Data._ID);
818             long id2 = o2.getAsLong(ContactsContract.Data._ID);
819             if (id1 == id2) return 0;
820             return (id1 < id2) ? -1 : 1;
821         }
822     }
823 
asSortedContentValuesArray( ArrayList<Entity.NamedContentValues> subValues)824     protected ContentValues[] asSortedContentValuesArray(
825             ArrayList<Entity.NamedContentValues> subValues) {
826         ContentValues[] result = new ContentValues[subValues.size()];
827         int i = 0;
828         for (Entity.NamedContentValues subValue : subValues) {
829             result[i] = subValue.values;
830             i++;
831         }
832         Arrays.sort(result, new IdComparator());
833         return result;
834     }
835 
assertDirty(Uri uri, boolean state)836     protected void assertDirty(Uri uri, boolean state) {
837         Cursor c = mResolver.query(uri, new String[]{"dirty"}, null, null, null);
838         assertTrue(c.moveToNext());
839         assertEquals(state, c.getLong(0) != 0);
840         assertFalse(c.moveToNext());
841         c.close();
842     }
843 
assertMetadataDirty(Uri uri, boolean state)844     protected void assertMetadataDirty(Uri uri, boolean state) {
845         Cursor c = mResolver.query(uri, new String[]{"metadata_dirty"}, null, null, null);
846         assertTrue(c.moveToNext());
847         assertEquals(state, c.getLong(0) != 0);
848         assertFalse(c.moveToNext());
849         c.close();
850     }
851 
getVersion(Uri uri)852     protected long getVersion(Uri uri) {
853         Cursor c = mResolver.query(uri, new String[]{"version"}, null, null, null);
854         assertTrue(c.moveToNext());
855         long version = c.getLong(0);
856         assertFalse(c.moveToNext());
857         c.close();
858         return version;
859     }
860 
clearDirty(Uri uri)861     protected void clearDirty(Uri uri) {
862         ContentValues values = new ContentValues();
863         values.put("dirty", 0);
864         mResolver.update(uri, values, null, null);
865     }
866 
clearMetadataDirty(Uri uri)867     protected void clearMetadataDirty(Uri uri) {
868         ContentValues values = new ContentValues();
869         values.put("metadata_dirty", 0);
870         mResolver.update(uri, values, null, null);
871     }
872 
storeValue(Uri contentUri, long id, String column, String value)873     protected void storeValue(Uri contentUri, long id, String column, String value) {
874         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
875     }
876 
storeValue(Uri contentUri, String column, String value)877     protected void storeValue(Uri contentUri, String column, String value) {
878         ContentValues values = new ContentValues();
879         values.put(column, value);
880 
881         mResolver.update(contentUri, values, null, null);
882     }
883 
storeValue(Uri contentUri, long id, String column, long value)884     protected void storeValue(Uri contentUri, long id, String column, long value) {
885         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
886     }
887 
storeValue(Uri contentUri, String column, long value)888     protected void storeValue(Uri contentUri, String column, long value) {
889         ContentValues values = new ContentValues();
890         values.put(column, value);
891 
892         mResolver.update(contentUri, values, null, null);
893     }
894 
assertStoredValue(Uri contentUri, long id, String column, Object expectedValue)895     protected void assertStoredValue(Uri contentUri, long id, String column, Object expectedValue) {
896         assertStoredValue(ContentUris.withAppendedId(contentUri, id), column, expectedValue);
897     }
898 
assertStoredValue(Uri rowUri, String column, Object expectedValue)899     protected void assertStoredValue(Uri rowUri, String column, Object expectedValue) {
900         String value = getStoredValue(rowUri, column);
901         if (expectedValue == null) {
902             assertNull("Column value " + column, value);
903         } else {
904             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
905         }
906     }
907 
assertStoredValue(Uri rowUri, String selection, String[] selectionArgs, String column, Object expectedValue)908     protected void assertStoredValue(Uri rowUri, String selection, String[] selectionArgs,
909             String column, Object expectedValue) {
910         String value = getStoredValue(rowUri, selection, selectionArgs, column);
911         if (expectedValue == null) {
912             assertNull("Column value " + column, value);
913         } else {
914             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
915         }
916     }
917 
getStoredValue(Uri rowUri, String column)918     protected String getStoredValue(Uri rowUri, String column) {
919         return getStoredValue(rowUri, null, null, column);
920     }
921 
getStoredValue(Uri uri, String selection, String[] selectionArgs, String column)922     protected String getStoredValue(Uri uri, String selection, String[] selectionArgs,
923             String column) {
924         String value = null;
925         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
926         try {
927             assertEquals("Record count for " + uri, 1, c.getCount());
928 
929             if (c.moveToFirst()) {
930                 value = c.getString(c.getColumnIndex(column));
931             }
932         } finally {
933             c.close();
934         }
935         return value;
936     }
937 
getStoredLongValue(Uri uri, String selection, String[] selectionArgs, String column)938     protected Long getStoredLongValue(Uri uri, String selection, String[] selectionArgs,
939             String column) {
940         Long value = null;
941         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
942         try {
943             assertEquals("Record count", 1, c.getCount());
944 
945             if (c.moveToFirst()) {
946                 value = c.getLong(c.getColumnIndex(column));
947             }
948         } finally {
949             c.close();
950         }
951         return value;
952     }
953 
getStoredLongValue(Uri uri, String column)954     protected Long getStoredLongValue(Uri uri, String column) {
955         return getStoredLongValue(uri, null, null, column);
956     }
957 
assertStoredValues(Uri rowUri, ContentValues expectedValues)958     protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) {
959         assertStoredValues(rowUri, null, null, expectedValues);
960     }
961 
assertStoredValues(Uri rowUri, ContentValues... expectedValues)962     protected void assertStoredValues(Uri rowUri, ContentValues... expectedValues) {
963         assertStoredValues(rowUri, null, null, expectedValues);
964     }
965 
assertStoredValues(Uri rowUri, String selection, String[] selectionArgs, ContentValues expectedValues)966     protected void assertStoredValues(Uri rowUri, String selection, String[] selectionArgs,
967             ContentValues expectedValues) {
968         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
969         try {
970             assertEquals("Record count", 1, c.getCount());
971             c.moveToFirst();
972             assertCursorValues(c, expectedValues);
973         } catch (Error e) {
974             TestUtils.dumpCursor(c);
975             throw e;
976         } finally {
977             c.close();
978         }
979     }
980 
assertContainsValues(Uri rowUri, ContentValues expectedValues)981     protected void assertContainsValues(Uri rowUri, ContentValues expectedValues) {
982         Cursor c = mResolver.query(rowUri, null, null, null, null);
983         try {
984             assertEquals("Record count", 1, c.getCount());
985             c.moveToFirst();
986             assertCursorValuesPartialMatch(c, expectedValues);
987         } catch (Error e) {
988             TestUtils.dumpCursor(c);
989             throw e;
990         } finally {
991             c.close();
992         }
993     }
994 
assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues)995     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues) {
996         assertStoredValuesWithProjection(rowUri, new ContentValues[] {expectedValues});
997     }
998 
assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues)999     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues) {
1000         assertTrue("Need at least one ContentValues for this test", expectedValues.length > 0);
1001         Cursor c = mResolver.query(rowUri, buildProjection(expectedValues[0]), null, null, null);
1002         try {
1003             assertEquals("Record count", expectedValues.length, c.getCount());
1004             c.moveToFirst();
1005             assertCursorValues(c, expectedValues);
1006         } catch (Error e) {
1007             TestUtils.dumpCursor(c);
1008             throw e;
1009         } finally {
1010             c.close();
1011         }
1012     }
1013 
assertStoredValues( Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)1014     protected void assertStoredValues(
1015             Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues) {
1016         assertStoredValues(mResolver.query(rowUri, null, selection, selectionArgs, null),
1017                 expectedValues);
1018     }
1019 
assertStoredValues(Cursor c, ContentValues... expectedValues)1020     private void assertStoredValues(Cursor c, ContentValues... expectedValues) {
1021         try {
1022             assertEquals("Record count", expectedValues.length, c.getCount());
1023             assertCursorValues(c, expectedValues);
1024         } catch (Error e) {
1025             TestUtils.dumpCursor(c);
1026             throw e;
1027         } finally {
1028             c.close();
1029         }
1030     }
1031 
1032     /**
1033      * A variation of {@link #assertStoredValues}, but it queries directly to the DB.
1034      */
assertStoredValuesDb( String sql, String[] selectionArgs, ContentValues... expectedValues)1035     protected void assertStoredValuesDb(
1036             String sql, String[] selectionArgs, ContentValues... expectedValues) {
1037         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
1038                 .getReadableDatabase();
1039         assertStoredValues(db.rawQuery(sql, selectionArgs), expectedValues);
1040     }
1041 
assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues)1042     protected void assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues) {
1043         assertStoredValuesOrderly(rowUri, null, null, expectedValues);
1044     }
1045 
assertStoredValuesOrderly(Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)1046     protected void assertStoredValuesOrderly(Uri rowUri, String selection,
1047             String[] selectionArgs, ContentValues... expectedValues) {
1048         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
1049         try {
1050             assertEquals("Record count", expectedValues.length, c.getCount());
1051             assertCursorValuesOrderly(c, expectedValues);
1052         } catch (Error e) {
1053             TestUtils.dumpCursor(c);
1054             throw e;
1055         } finally {
1056             c.close();
1057         }
1058     }
1059 
1060     /**
1061      * Constructs a selection (where clause) out of all supplied values, uses it
1062      * to query the provider and verifies that a single row is returned and it
1063      * has the same values as requested.
1064      */
assertSelection(Uri uri, ContentValues values, String idColumn, long id)1065     protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) {
1066         assertSelection(uri, values, idColumn, id, null);
1067     }
1068 
assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn, long id)1069     public void assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn,
1070             long id) {
1071         assertSelection(uri, values, idColumn, id, buildProjection(values));
1072     }
1073 
assertSelection(Uri uri, ContentValues values, String idColumn, long id, String[] projection)1074     private void assertSelection(Uri uri, ContentValues values, String idColumn, long id,
1075             String[] projection) {
1076         StringBuilder sb = new StringBuilder();
1077         ArrayList<String> selectionArgs = new ArrayList<String>(values.size());
1078         if (idColumn != null) {
1079             sb.append(idColumn).append("=").append(id);
1080         }
1081         Set<Map.Entry<String, Object>> entries = values.valueSet();
1082         for (Map.Entry<String, Object> entry : entries) {
1083             String column = entry.getKey();
1084             Object value = entry.getValue();
1085             if (sb.length() != 0) {
1086                 sb.append(" AND ");
1087             }
1088             sb.append(column);
1089             if (value == null) {
1090                 sb.append(" IS NULL");
1091             } else {
1092                 sb.append("=?");
1093                 selectionArgs.add(String.valueOf(value));
1094             }
1095         }
1096 
1097         Cursor c = mResolver.query(uri, projection, sb.toString(), selectionArgs.toArray(new String[0]),
1098                 null);
1099         try {
1100             assertEquals("Record count", 1, c.getCount());
1101             c.moveToFirst();
1102             assertCursorValues(c, values);
1103         } catch (Error e) {
1104             TestUtils.dumpCursor(c);
1105 
1106             // Dump with no selection.
1107             TestUtils.dumpUri(mResolver, uri);
1108             throw e;
1109         } finally {
1110             c.close();
1111         }
1112     }
1113 
assertCursorValue(Cursor cursor, String column, Object expectedValue)1114     protected void assertCursorValue(Cursor cursor, String column, Object expectedValue) {
1115         String actualValue = cursor.getString(cursor.getColumnIndex(column));
1116         assertEquals("Column " + column, String.valueOf(expectedValue),
1117                 String.valueOf(actualValue));
1118     }
1119 
assertCursorValues(Cursor cursor, ContentValues expectedValues)1120     protected void assertCursorValues(Cursor cursor, ContentValues expectedValues) {
1121         StringBuilder message = new StringBuilder();
1122         boolean result = equalsWithExpectedValues(cursor, expectedValues, message);
1123         assertTrue(message.toString(), result);
1124     }
1125 
assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues)1126     protected void assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues) {
1127         StringBuilder message = new StringBuilder();
1128         boolean result = expectedValuePartiallyMatches(cursor, expectedValues, message);
1129         assertTrue(message.toString(), result);
1130     }
1131 
assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues)1132     protected void assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues) {
1133         final StringBuilder message = new StringBuilder();
1134         boolean found = false;
1135         cursor.moveToPosition(-1);
1136         while (cursor.moveToNext()) {
1137             message.setLength(0);
1138             final int pos = cursor.getPosition();
1139             found = equalsWithExpectedValues(cursor, expectedValues, message);
1140             if (found) {
1141                 break;
1142             }
1143         }
1144         assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(),
1145                 found);
1146     }
1147 
assertCursorValues(Cursor cursor, ContentValues... expectedValues)1148     protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) {
1149         StringBuilder message = new StringBuilder();
1150 
1151         // In case if expectedValues contains multiple identical values, remember which cursor
1152         // rows are "consumed" to prevent multiple ContentValues from hitting the same row.
1153         final BitSet used = new BitSet(cursor.getCount());
1154 
1155         for (ContentValues v : expectedValues) {
1156             boolean found = false;
1157             cursor.moveToPosition(-1);
1158             while (cursor.moveToNext()) {
1159                 final int pos = cursor.getPosition();
1160                 if (used.get(pos)) continue;
1161                 found = equalsWithExpectedValues(cursor, v, message);
1162                 if (found) {
1163                     used.set(pos);
1164                     break;
1165                 }
1166             }
1167             assertTrue("Expected values can not be found " + v + "," + message.toString(), found);
1168         }
1169     }
1170 
assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues)1171     public static void assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues) {
1172         StringBuilder message = new StringBuilder();
1173         cursor.moveToPosition(-1);
1174         for (ContentValues v : expectedValues) {
1175             assertTrue(cursor.moveToNext());
1176             boolean ok = equalsWithExpectedValues(cursor, v, message);
1177             assertTrue("ContentValues didn't match.  Pos=" + cursor.getPosition() + ", values=" +
1178                     v + message.toString(), ok);
1179         }
1180     }
1181 
expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1182     private boolean expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues,
1183             StringBuilder msgBuffer) {
1184         for (String column : expectedValues.keySet()) {
1185             int index = cursor.getColumnIndex(column);
1186             if (index == -1) {
1187                 msgBuffer.append(" No such column: ").append(column);
1188                 return false;
1189             }
1190             String expectedValue = expectedValues.getAsString(column);
1191             String value = cursor.getString(cursor.getColumnIndex(column));
1192             if (value != null && !value.contains(expectedValue)) {
1193                 msgBuffer.append(" Column value ").append(column).append(" expected to contain <")
1194                         .append(expectedValue).append(">, but was <").append(value).append('>');
1195                 return false;
1196             }
1197         }
1198         return true;
1199     }
1200 
equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1201     private static boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
1202             StringBuilder msgBuffer) {
1203         for (String column : expectedValues.keySet()) {
1204             int index = cursor.getColumnIndex(column);
1205             if (index == -1) {
1206                 msgBuffer.append(" No such column: ").append(column);
1207                 return false;
1208             }
1209             Object expectedValue = expectedValues.get(column);
1210             String value;
1211             if (expectedValue instanceof byte[]) {
1212                 expectedValue = Hex.encodeHex((byte[])expectedValue, false);
1213                 value = Hex.encodeHex(cursor.getBlob(index), false);
1214             } else {
1215                 expectedValue = expectedValues.getAsString(column);
1216                 value = cursor.getString(cursor.getColumnIndex(column));
1217             }
1218             if (expectedValue != null && !expectedValue.equals(value) || value != null
1219                     && !value.equals(expectedValue)) {
1220                 msgBuffer
1221                         .append(" Column value ")
1222                         .append(column)
1223                         .append(" expected <")
1224                         .append(expectedValue)
1225                         .append(">, but was <")
1226                         .append(value)
1227                         .append('>');
1228                 return false;
1229             }
1230         }
1231         return true;
1232     }
1233 
1234     private static final String[] DATA_USAGE_PROJECTION =
1235             new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED};
1236 
assertDataUsageZero(Uri uri, String data1)1237     protected void assertDataUsageZero(Uri uri, String data1) {
1238         final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null,
1239                 null);
1240         try {
1241             dumpCursor(cursor);
1242             assertCursorHasAnyRecordMatch(cursor, cv(Data.DATA1, data1, Data.TIMES_USED, 0,
1243                     Data.LAST_TIME_USED, 0));
1244         } finally {
1245             cursor.close();
1246         }
1247     }
1248 
buildProjection(ContentValues values)1249     private String[] buildProjection(ContentValues values) {
1250         String[] projection = new String[values.size()];
1251         Iterator<Entry<String, Object>> iter = values.valueSet().iterator();
1252         for (int i = 0; i < projection.length; i++) {
1253             projection[i] = iter.next().getKey();
1254         }
1255         return projection;
1256     }
1257 
getCount(Uri uri)1258     protected int getCount(Uri uri) {
1259         return getCount(uri, null, null);
1260     }
1261 
getCount(Uri uri, String selection, String[] selectionArgs)1262     protected int getCount(Uri uri, String selection, String[] selectionArgs) {
1263         Cursor c = mResolver.query(uri, null, selection, selectionArgs, null);
1264         try {
1265             return c.getCount();
1266         } finally {
1267             c.close();
1268         }
1269     }
1270 
dump(ContentResolver resolver, boolean aggregatedOnly)1271     public static void dump(ContentResolver resolver, boolean aggregatedOnly) {
1272         String[] projection = new String[] {
1273                 Contacts._ID,
1274                 Contacts.DISPLAY_NAME
1275         };
1276         String selection = null;
1277         if (aggregatedOnly) {
1278             selection = Contacts._ID
1279                     + " IN (SELECT contact_id" +
1280                     		" FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)";
1281         }
1282 
1283         Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null,
1284                 Contacts.DISPLAY_NAME);
1285         while(c.moveToNext()) {
1286             long contactId = c.getLong(0);
1287             Log.i("Contact   ", String.format("%5d %s", contactId, c.getString(1)));
1288             dumpRawContacts(resolver, contactId);
1289             Log.i("          ", ".");
1290         }
1291         c.close();
1292     }
1293 
dumpRawContacts(ContentResolver resolver, long contactId)1294     private static void dumpRawContacts(ContentResolver resolver, long contactId) {
1295         String[] projection = new String[] {
1296                 RawContacts._ID,
1297         };
1298         Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "="
1299                 + contactId, null, null);
1300         while(c.moveToNext()) {
1301             long rawContactId = c.getLong(0);
1302             Log.i("RawContact", String.format("      %-5d", rawContactId));
1303             dumpData(resolver, rawContactId);
1304         }
1305         c.close();
1306     }
1307 
dumpData(ContentResolver resolver, long rawContactId)1308     private static void dumpData(ContentResolver resolver, long rawContactId) {
1309         String[] projection = new String[] {
1310                 Data.MIMETYPE,
1311                 Data.DATA1,
1312                 Data.DATA2,
1313                 Data.DATA3,
1314         };
1315         Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "="
1316                 + rawContactId, null, Data.MIMETYPE);
1317         while(c.moveToNext()) {
1318             String mimetype = c.getString(0);
1319             if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
1320                 Log.i("Photo     ", "");
1321             } else {
1322                 mimetype = mimetype.substring(mimetype.indexOf('/') + 1);
1323                 Log.i("Data      ", String.format("            %-10s %s,%s,%s", mimetype,
1324                         c.getString(1), c.getString(2), c.getString(3)));
1325             }
1326         }
1327         c.close();
1328     }
1329 
assertNetworkNotified(boolean expected)1330     protected void assertNetworkNotified(boolean expected) {
1331         assertEquals(expected, (getContactsProvider()).isNetworkNotified());
1332     }
1333 
assertMetadataNetworkNotified(boolean expected)1334     protected void assertMetadataNetworkNotified(boolean expected) {
1335         assertEquals(expected, (getContactsProvider()).isMetadataNetworkNotified());
1336     }
1337 
assertProjection(Uri uri, String[] expectedProjection)1338     protected void assertProjection(Uri uri, String[] expectedProjection) {
1339         Cursor cursor = mResolver.query(uri, null, "0", null, null);
1340         String[] actualProjection = cursor.getColumnNames();
1341         MoreAsserts.assertEquals("Incorrect projection for URI: " + uri,
1342                 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection));
1343         cursor.close();
1344     }
1345 
assertContainProjection(Uri uri, String[] mustHaveProjection)1346     protected void assertContainProjection(Uri uri, String[] mustHaveProjection) {
1347         Cursor cursor = mResolver.query(uri, null, "0", null, null);
1348         String[] actualProjection = cursor.getColumnNames();
1349         Set<String> actualProjectionSet = Sets.newHashSet(actualProjection);
1350         Set<String> mustHaveProjectionSet = Sets.newHashSet(mustHaveProjection);
1351         actualProjectionSet.retainAll(mustHaveProjectionSet);
1352         MoreAsserts.assertEquals(mustHaveProjectionSet, actualProjectionSet);
1353         cursor.close();
1354     }
1355 
assertRowCount(int expectedCount, Uri uri, String selection, String[] args)1356     protected void assertRowCount(int expectedCount, Uri uri, String selection, String[] args) {
1357         Cursor cursor = mResolver.query(uri, null, selection, args, null);
1358 
1359         try {
1360             assertEquals(expectedCount, cursor.getCount());
1361         } catch (Error e) {
1362             TestUtils.dumpCursor(cursor);
1363             throw e;
1364         } finally {
1365             cursor.close();
1366         }
1367     }
1368 
assertLastModified(Uri uri, long time)1369     protected void assertLastModified(Uri uri, long time) {
1370         Cursor c = mResolver.query(uri, null, null, null, null);
1371         c.moveToFirst();
1372         int index = c.getColumnIndex(CallLog.Calls.LAST_MODIFIED);
1373         long timeStamp = c.getLong(index);
1374         assertEquals(timeStamp, time);
1375     }
1376 
setTimeForTest(Long time)1377     protected void setTimeForTest(Long time) {
1378         Uri uri = Calls.CONTENT_URI.buildUpon()
1379                 .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1")
1380                 .appendQueryParameter(CallLogProvider.PARAM_KEY_SET_TIME_FOR_TESTING,
1381                         time == null ? "null" : time.toString())
1382                 .build();
1383         mResolver.query(uri, null, null, null, null);
1384     }
1385 
insertRawContact(ContentValues values)1386     protected Uri insertRawContact(ContentValues values) {
1387         return TestUtils.insertRawContact(mResolver,
1388                 getContactsProvider().getDatabaseHelper(), values);
1389     }
1390 
insertProfileRawContact(ContentValues values)1391     protected Uri insertProfileRawContact(ContentValues values) {
1392         return TestUtils.insertProfileRawContact(mResolver,
1393                 getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values);
1394     }
1395 
1396     /**
1397      * A contact in the database, and the attributes used to create it.  Construct using
1398      * {@link GoldenContactBuilder#build()}.
1399      */
1400     public final class GoldenContact {
1401 
1402         private final long rawContactId;
1403 
1404         private final long contactId;
1405 
1406         private final String givenName;
1407 
1408         private final String familyName;
1409 
1410         private final String nickname;
1411 
1412         private final byte[] photo;
1413 
1414         private final String company;
1415 
1416         private final String title;
1417 
1418         private final String phone;
1419 
1420         private final String email;
1421 
GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId)1422         private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) {
1423 
1424             this.rawContactId = rawContactId;
1425             this.contactId = contactId;
1426             givenName = builder.givenName;
1427             familyName = builder.familyName;
1428             nickname = builder.nickname;
1429             photo = builder.photo;
1430             company = builder.company;
1431             title = builder.title;
1432             phone = builder.phone;
1433             email = builder.email;
1434         }
1435 
delete()1436         public void delete() {
1437             Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1438             mResolver.delete(rawContactUri, null, null);
1439         }
1440 
1441         /**
1442          * Returns the index of the contact in table "raw_contacts"
1443          */
getRawContactId()1444         public long getRawContactId() {
1445             return rawContactId;
1446         }
1447 
1448         /**
1449          * Returns the index of the contact in table "contacts"
1450          */
getContactId()1451         public long getContactId() {
1452             return contactId;
1453         }
1454 
1455         /**
1456          * Returns the lookup key for the contact.
1457          */
getLookupKey()1458         public String getLookupKey() {
1459             return queryLookupKey(contactId);
1460         }
1461 
1462         /**
1463          * Returns the contact's given name.
1464          */
getGivenName()1465         public String getGivenName() {
1466             return givenName;
1467         }
1468 
1469         /**
1470          * Returns the contact's family name.
1471          */
getFamilyName()1472         public String getFamilyName() {
1473             return familyName;
1474         }
1475 
1476         /**
1477          * Returns the contact's nickname.
1478          */
getNickname()1479         public String getNickname() {
1480             return nickname;
1481         }
1482 
1483         /**
1484          * Return's the contact's photo
1485          */
getPhoto()1486         public byte[] getPhoto() {
1487             return photo;
1488         }
1489 
1490         /**
1491          * Return's the company at which the contact works.
1492          */
getCompany()1493         public String getCompany() {
1494             return company;
1495         }
1496 
1497         /**
1498          * Returns the contact's job title.
1499          */
getTitle()1500         public String getTitle() {
1501             return title;
1502         }
1503 
1504         /**
1505          * Returns the contact's phone number
1506          */
getPhone()1507         public String getPhone() {
1508             return phone;
1509         }
1510 
1511         /**
1512          * Returns the contact's email address
1513          */
getEmail()1514         public String getEmail() {
1515             return email;
1516         }
1517      }
1518 
1519     /**
1520      * Builds {@link GoldenContact} objects.  Unspecified boolean objects default to false.
1521      * Unspecified String objects default to null.
1522      */
1523     public final class GoldenContactBuilder {
1524 
1525         private String givenName;
1526 
1527         private String familyName;
1528 
1529         private String nickname;
1530 
1531         private byte[] photo;
1532 
1533         private String company;
1534 
1535         private String title;
1536 
1537         private String phone;
1538 
1539         private String email;
1540 
1541         /**
1542          * The contact's given and family names.
1543          *
1544          * TODO(dplotnikov): inline, or should we require them to set both names if they set either?
1545          */
name(String givenName, String familyName)1546         public GoldenContactBuilder name(String givenName, String familyName) {
1547             return givenName(givenName).familyName(familyName);
1548         }
1549 
1550         /**
1551          * The contact's given name.
1552          */
givenName(String value)1553         public GoldenContactBuilder givenName(String value) {
1554             givenName = value;
1555             return this;
1556         }
1557 
1558         /**
1559          * The contact's family name.
1560          */
familyName(String value)1561         public GoldenContactBuilder familyName(String value) {
1562             familyName = value;
1563             return this;
1564         }
1565 
1566         /**
1567          * The contact's nickname.
1568          */
nickname(String value)1569         public GoldenContactBuilder nickname(String value) {
1570             nickname = value;
1571             return this;
1572         }
1573 
1574         /**
1575          * The contact's photo.
1576          */
photo(byte[] value)1577         public GoldenContactBuilder photo(byte[] value) {
1578             photo = value;
1579             return this;
1580         }
1581 
1582         /**
1583          * The company at which the contact works.
1584          */
company(String value)1585         public GoldenContactBuilder company(String value) {
1586             company = value;
1587             return this;
1588         }
1589 
1590         /**
1591          * The contact's job title.
1592          */
title(String value)1593         public GoldenContactBuilder title(String value) {
1594             title = value;
1595             return this;
1596         }
1597 
1598         /**
1599          * The contact's phone number.
1600          */
phone(String value)1601         public GoldenContactBuilder phone(String value) {
1602             phone = value;
1603             return this;
1604         }
1605 
1606         /**
1607          * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE}
1608          * with a presence of "Coding for Android".
1609          */
email(String value)1610         public GoldenContactBuilder email(String value) {
1611             email = value;
1612             return this;
1613         }
1614 
1615         /**
1616          * Builds the {@link GoldenContact} specified by this builder.
1617          */
build()1618         public GoldenContact build() {
1619 
1620             final long groupId = createGroup(mAccount, "gsid1", "title1");
1621 
1622             long rawContactId = RawContactUtil.createRawContact(mResolver);
1623             insertGroupMembership(rawContactId, groupId);
1624 
1625             if (givenName != null || familyName != null) {
1626                 DataUtil.insertStructuredName(mResolver, rawContactId, givenName, familyName);
1627             }
1628             if (nickname != null) {
1629                 insertNickname(rawContactId, nickname);
1630             }
1631             if (photo != null) {
1632                 insertPhoto(rawContactId);
1633             }
1634             if (company != null || title != null) {
1635                 insertOrganization(rawContactId);
1636             }
1637             if (email != null) {
1638                 insertEmail(rawContactId);
1639             }
1640             if (phone != null) {
1641                 insertPhone(rawContactId);
1642             }
1643 
1644             long contactId = queryContactId(rawContactId);
1645 
1646             return new GoldenContact(this, rawContactId, contactId);
1647         }
1648 
insertPhoto(long rawContactId)1649         private void insertPhoto(long rawContactId) {
1650             ContentValues values = new ContentValues();
1651             values.put(Data.RAW_CONTACT_ID, rawContactId);
1652             values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1653             values.put(Photo.PHOTO, photo);
1654             mResolver.insert(Data.CONTENT_URI, values);
1655         }
1656 
insertOrganization(long rawContactId)1657         private void insertOrganization(long rawContactId) {
1658 
1659             ContentValues values = new ContentValues();
1660             values.put(Data.RAW_CONTACT_ID, rawContactId);
1661             values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1662             values.put(Organization.TYPE, Organization.TYPE_WORK);
1663             if (company != null) {
1664                 values.put(Organization.COMPANY, company);
1665             }
1666             if (title != null) {
1667                 values.put(Organization.TITLE, title);
1668             }
1669             mResolver.insert(Data.CONTENT_URI, values);
1670         }
1671 
insertEmail(long rawContactId)1672         private void insertEmail(long rawContactId) {
1673 
1674             ContentValues values = new ContentValues();
1675             values.put(Data.RAW_CONTACT_ID, rawContactId);
1676             values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1677             values.put(Email.TYPE, Email.TYPE_WORK);
1678             values.put(Email.DATA, "foo@acme.com");
1679             mResolver.insert(Data.CONTENT_URI, values);
1680 
1681             int protocol = Im.PROTOCOL_GOOGLE_TALK;
1682 
1683             values.clear();
1684             values.put(StatusUpdates.PROTOCOL, protocol);
1685             values.put(StatusUpdates.IM_HANDLE, email);
1686             values.put(StatusUpdates.IM_ACCOUNT, "foo");
1687             values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE);
1688             values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1689             values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android");
1690             mResolver.insert(StatusUpdates.CONTENT_URI, values);
1691         }
1692 
insertPhone(long rawContactId)1693         private void insertPhone(long rawContactId) {
1694             ContentValues values = new ContentValues();
1695             values.put(Data.RAW_CONTACT_ID, rawContactId);
1696             values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1697             values.put(Data.IS_PRIMARY, 1);
1698             values.put(Phone.TYPE, Phone.TYPE_HOME);
1699             values.put(Phone.NUMBER, phone);
1700             mResolver.insert(Data.CONTENT_URI, values);
1701         }
1702     }
1703 }
1704