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 
assertDataUsageCursorContains(Uri uri, String data1, int timesUsed, int lastTimeUsed)1237     protected void assertDataUsageCursorContains(Uri uri, String data1, int timesUsed,
1238             int lastTimeUsed) {
1239         final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null,
1240                 null);
1241         try {
1242             dumpCursor(cursor);
1243             assertCursorHasAnyRecordMatch(cursor, cv(Data.DATA1, data1, Data.TIMES_USED, timesUsed,
1244                     Data.LAST_TIME_USED, lastTimeUsed));
1245         } finally {
1246             cursor.close();
1247         }
1248     }
1249 
buildProjection(ContentValues values)1250     private String[] buildProjection(ContentValues values) {
1251         String[] projection = new String[values.size()];
1252         Iterator<Entry<String, Object>> iter = values.valueSet().iterator();
1253         for (int i = 0; i < projection.length; i++) {
1254             projection[i] = iter.next().getKey();
1255         }
1256         return projection;
1257     }
1258 
getCount(Uri uri)1259     protected int getCount(Uri uri) {
1260         return getCount(uri, null, null);
1261     }
1262 
getCount(Uri uri, String selection, String[] selectionArgs)1263     protected int getCount(Uri uri, String selection, String[] selectionArgs) {
1264         Cursor c = mResolver.query(uri, null, selection, selectionArgs, null);
1265         try {
1266             return c.getCount();
1267         } finally {
1268             c.close();
1269         }
1270     }
1271 
dump(ContentResolver resolver, boolean aggregatedOnly)1272     public static void dump(ContentResolver resolver, boolean aggregatedOnly) {
1273         String[] projection = new String[] {
1274                 Contacts._ID,
1275                 Contacts.DISPLAY_NAME
1276         };
1277         String selection = null;
1278         if (aggregatedOnly) {
1279             selection = Contacts._ID
1280                     + " IN (SELECT contact_id" +
1281                     		" FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)";
1282         }
1283 
1284         Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null,
1285                 Contacts.DISPLAY_NAME);
1286         while(c.moveToNext()) {
1287             long contactId = c.getLong(0);
1288             Log.i("Contact   ", String.format("%5d %s", contactId, c.getString(1)));
1289             dumpRawContacts(resolver, contactId);
1290             Log.i("          ", ".");
1291         }
1292         c.close();
1293     }
1294 
dumpRawContacts(ContentResolver resolver, long contactId)1295     private static void dumpRawContacts(ContentResolver resolver, long contactId) {
1296         String[] projection = new String[] {
1297                 RawContacts._ID,
1298         };
1299         Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "="
1300                 + contactId, null, null);
1301         while(c.moveToNext()) {
1302             long rawContactId = c.getLong(0);
1303             Log.i("RawContact", String.format("      %-5d", rawContactId));
1304             dumpData(resolver, rawContactId);
1305         }
1306         c.close();
1307     }
1308 
dumpData(ContentResolver resolver, long rawContactId)1309     private static void dumpData(ContentResolver resolver, long rawContactId) {
1310         String[] projection = new String[] {
1311                 Data.MIMETYPE,
1312                 Data.DATA1,
1313                 Data.DATA2,
1314                 Data.DATA3,
1315         };
1316         Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "="
1317                 + rawContactId, null, Data.MIMETYPE);
1318         while(c.moveToNext()) {
1319             String mimetype = c.getString(0);
1320             if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
1321                 Log.i("Photo     ", "");
1322             } else {
1323                 mimetype = mimetype.substring(mimetype.indexOf('/') + 1);
1324                 Log.i("Data      ", String.format("            %-10s %s,%s,%s", mimetype,
1325                         c.getString(1), c.getString(2), c.getString(3)));
1326             }
1327         }
1328         c.close();
1329     }
1330 
assertNetworkNotified(boolean expected)1331     protected void assertNetworkNotified(boolean expected) {
1332         assertEquals(expected, (getContactsProvider()).isNetworkNotified());
1333     }
1334 
assertMetadataNetworkNotified(boolean expected)1335     protected void assertMetadataNetworkNotified(boolean expected) {
1336         assertEquals(expected, (getContactsProvider()).isMetadataNetworkNotified());
1337     }
1338 
assertProjection(Uri uri, String[] expectedProjection)1339     protected void assertProjection(Uri uri, String[] expectedProjection) {
1340         Cursor cursor = mResolver.query(uri, null, "0", null, null);
1341         String[] actualProjection = cursor.getColumnNames();
1342         MoreAsserts.assertEquals("Incorrect projection for URI: " + uri,
1343                 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection));
1344         cursor.close();
1345     }
1346 
assertContainProjection(Uri uri, String[] mustHaveProjection)1347     protected void assertContainProjection(Uri uri, String[] mustHaveProjection) {
1348         Cursor cursor = mResolver.query(uri, null, "0", null, null);
1349         String[] actualProjection = cursor.getColumnNames();
1350         Set<String> actualProjectionSet = Sets.newHashSet(actualProjection);
1351         Set<String> mustHaveProjectionSet = Sets.newHashSet(mustHaveProjection);
1352         actualProjectionSet.retainAll(mustHaveProjectionSet);
1353         MoreAsserts.assertEquals(mustHaveProjectionSet, actualProjectionSet);
1354         cursor.close();
1355     }
1356 
assertRowCount(int expectedCount, Uri uri, String selection, String[] args)1357     protected void assertRowCount(int expectedCount, Uri uri, String selection, String[] args) {
1358         Cursor cursor = mResolver.query(uri, null, selection, args, null);
1359 
1360         try {
1361             assertEquals(expectedCount, cursor.getCount());
1362         } catch (Error e) {
1363             TestUtils.dumpCursor(cursor);
1364             throw e;
1365         } finally {
1366             cursor.close();
1367         }
1368     }
1369 
assertLastModified(Uri uri, long time)1370     protected void assertLastModified(Uri uri, long time) {
1371         Cursor c = mResolver.query(uri, null, null, null, null);
1372         c.moveToFirst();
1373         int index = c.getColumnIndex(CallLog.Calls.LAST_MODIFIED);
1374         long timeStamp = c.getLong(index);
1375         assertEquals(timeStamp, time);
1376     }
1377 
setTimeForTest(Long time)1378     protected void setTimeForTest(Long time) {
1379         Uri uri = Calls.CONTENT_URI.buildUpon()
1380                 .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1")
1381                 .appendQueryParameter(CallLogProvider.PARAM_KEY_SET_TIME_FOR_TESTING,
1382                         time == null ? "null" : time.toString())
1383                 .build();
1384         mResolver.query(uri, null, null, null, null);
1385     }
1386 
insertRawContact(ContentValues values)1387     protected Uri insertRawContact(ContentValues values) {
1388         return TestUtils.insertRawContact(mResolver,
1389                 getContactsProvider().getDatabaseHelper(), values);
1390     }
1391 
insertProfileRawContact(ContentValues values)1392     protected Uri insertProfileRawContact(ContentValues values) {
1393         return TestUtils.insertProfileRawContact(mResolver,
1394                 getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values);
1395     }
1396 
1397     /**
1398      * A contact in the database, and the attributes used to create it.  Construct using
1399      * {@link GoldenContactBuilder#build()}.
1400      */
1401     public final class GoldenContact {
1402 
1403         private final long rawContactId;
1404 
1405         private final long contactId;
1406 
1407         private final String givenName;
1408 
1409         private final String familyName;
1410 
1411         private final String nickname;
1412 
1413         private final byte[] photo;
1414 
1415         private final String company;
1416 
1417         private final String title;
1418 
1419         private final String phone;
1420 
1421         private final String email;
1422 
GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId)1423         private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) {
1424 
1425             this.rawContactId = rawContactId;
1426             this.contactId = contactId;
1427             givenName = builder.givenName;
1428             familyName = builder.familyName;
1429             nickname = builder.nickname;
1430             photo = builder.photo;
1431             company = builder.company;
1432             title = builder.title;
1433             phone = builder.phone;
1434             email = builder.email;
1435         }
1436 
delete()1437         public void delete() {
1438             Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1439             mResolver.delete(rawContactUri, null, null);
1440         }
1441 
1442         /**
1443          * Returns the index of the contact in table "raw_contacts"
1444          */
getRawContactId()1445         public long getRawContactId() {
1446             return rawContactId;
1447         }
1448 
1449         /**
1450          * Returns the index of the contact in table "contacts"
1451          */
getContactId()1452         public long getContactId() {
1453             return contactId;
1454         }
1455 
1456         /**
1457          * Returns the lookup key for the contact.
1458          */
getLookupKey()1459         public String getLookupKey() {
1460             return queryLookupKey(contactId);
1461         }
1462 
1463         /**
1464          * Returns the contact's given name.
1465          */
getGivenName()1466         public String getGivenName() {
1467             return givenName;
1468         }
1469 
1470         /**
1471          * Returns the contact's family name.
1472          */
getFamilyName()1473         public String getFamilyName() {
1474             return familyName;
1475         }
1476 
1477         /**
1478          * Returns the contact's nickname.
1479          */
getNickname()1480         public String getNickname() {
1481             return nickname;
1482         }
1483 
1484         /**
1485          * Return's the contact's photo
1486          */
getPhoto()1487         public byte[] getPhoto() {
1488             return photo;
1489         }
1490 
1491         /**
1492          * Return's the company at which the contact works.
1493          */
getCompany()1494         public String getCompany() {
1495             return company;
1496         }
1497 
1498         /**
1499          * Returns the contact's job title.
1500          */
getTitle()1501         public String getTitle() {
1502             return title;
1503         }
1504 
1505         /**
1506          * Returns the contact's phone number
1507          */
getPhone()1508         public String getPhone() {
1509             return phone;
1510         }
1511 
1512         /**
1513          * Returns the contact's email address
1514          */
getEmail()1515         public String getEmail() {
1516             return email;
1517         }
1518      }
1519 
1520     /**
1521      * Builds {@link GoldenContact} objects.  Unspecified boolean objects default to false.
1522      * Unspecified String objects default to null.
1523      */
1524     public final class GoldenContactBuilder {
1525 
1526         private String givenName;
1527 
1528         private String familyName;
1529 
1530         private String nickname;
1531 
1532         private byte[] photo;
1533 
1534         private String company;
1535 
1536         private String title;
1537 
1538         private String phone;
1539 
1540         private String email;
1541 
1542         /**
1543          * The contact's given and family names.
1544          *
1545          * TODO(dplotnikov): inline, or should we require them to set both names if they set either?
1546          */
name(String givenName, String familyName)1547         public GoldenContactBuilder name(String givenName, String familyName) {
1548             return givenName(givenName).familyName(familyName);
1549         }
1550 
1551         /**
1552          * The contact's given name.
1553          */
givenName(String value)1554         public GoldenContactBuilder givenName(String value) {
1555             givenName = value;
1556             return this;
1557         }
1558 
1559         /**
1560          * The contact's family name.
1561          */
familyName(String value)1562         public GoldenContactBuilder familyName(String value) {
1563             familyName = value;
1564             return this;
1565         }
1566 
1567         /**
1568          * The contact's nickname.
1569          */
nickname(String value)1570         public GoldenContactBuilder nickname(String value) {
1571             nickname = value;
1572             return this;
1573         }
1574 
1575         /**
1576          * The contact's photo.
1577          */
photo(byte[] value)1578         public GoldenContactBuilder photo(byte[] value) {
1579             photo = value;
1580             return this;
1581         }
1582 
1583         /**
1584          * The company at which the contact works.
1585          */
company(String value)1586         public GoldenContactBuilder company(String value) {
1587             company = value;
1588             return this;
1589         }
1590 
1591         /**
1592          * The contact's job title.
1593          */
title(String value)1594         public GoldenContactBuilder title(String value) {
1595             title = value;
1596             return this;
1597         }
1598 
1599         /**
1600          * The contact's phone number.
1601          */
phone(String value)1602         public GoldenContactBuilder phone(String value) {
1603             phone = value;
1604             return this;
1605         }
1606 
1607         /**
1608          * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE}
1609          * with a presence of "Coding for Android".
1610          */
email(String value)1611         public GoldenContactBuilder email(String value) {
1612             email = value;
1613             return this;
1614         }
1615 
1616         /**
1617          * Builds the {@link GoldenContact} specified by this builder.
1618          */
build()1619         public GoldenContact build() {
1620 
1621             final long groupId = createGroup(mAccount, "gsid1", "title1");
1622 
1623             long rawContactId = RawContactUtil.createRawContact(mResolver);
1624             insertGroupMembership(rawContactId, groupId);
1625 
1626             if (givenName != null || familyName != null) {
1627                 DataUtil.insertStructuredName(mResolver, rawContactId, givenName, familyName);
1628             }
1629             if (nickname != null) {
1630                 insertNickname(rawContactId, nickname);
1631             }
1632             if (photo != null) {
1633                 insertPhoto(rawContactId);
1634             }
1635             if (company != null || title != null) {
1636                 insertOrganization(rawContactId);
1637             }
1638             if (email != null) {
1639                 insertEmail(rawContactId);
1640             }
1641             if (phone != null) {
1642                 insertPhone(rawContactId);
1643             }
1644 
1645             long contactId = queryContactId(rawContactId);
1646 
1647             return new GoldenContact(this, rawContactId, contactId);
1648         }
1649 
insertPhoto(long rawContactId)1650         private void insertPhoto(long rawContactId) {
1651             ContentValues values = new ContentValues();
1652             values.put(Data.RAW_CONTACT_ID, rawContactId);
1653             values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1654             values.put(Photo.PHOTO, photo);
1655             mResolver.insert(Data.CONTENT_URI, values);
1656         }
1657 
insertOrganization(long rawContactId)1658         private void insertOrganization(long rawContactId) {
1659 
1660             ContentValues values = new ContentValues();
1661             values.put(Data.RAW_CONTACT_ID, rawContactId);
1662             values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1663             values.put(Organization.TYPE, Organization.TYPE_WORK);
1664             if (company != null) {
1665                 values.put(Organization.COMPANY, company);
1666             }
1667             if (title != null) {
1668                 values.put(Organization.TITLE, title);
1669             }
1670             mResolver.insert(Data.CONTENT_URI, values);
1671         }
1672 
insertEmail(long rawContactId)1673         private void insertEmail(long rawContactId) {
1674 
1675             ContentValues values = new ContentValues();
1676             values.put(Data.RAW_CONTACT_ID, rawContactId);
1677             values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1678             values.put(Email.TYPE, Email.TYPE_WORK);
1679             values.put(Email.DATA, "foo@acme.com");
1680             mResolver.insert(Data.CONTENT_URI, values);
1681 
1682             int protocol = Im.PROTOCOL_GOOGLE_TALK;
1683 
1684             values.clear();
1685             values.put(StatusUpdates.PROTOCOL, protocol);
1686             values.put(StatusUpdates.IM_HANDLE, email);
1687             values.put(StatusUpdates.IM_ACCOUNT, "foo");
1688             values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE);
1689             values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1690             values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android");
1691             mResolver.insert(StatusUpdates.CONTENT_URI, values);
1692         }
1693 
insertPhone(long rawContactId)1694         private void insertPhone(long rawContactId) {
1695             ContentValues values = new ContentValues();
1696             values.put(Data.RAW_CONTACT_ID, rawContactId);
1697             values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1698             values.put(Data.IS_PRIMARY, 1);
1699             values.put(Phone.TYPE, Phone.TYPE_HOME);
1700             values.put(Phone.NUMBER, phone);
1701             mResolver.insert(Data.CONTENT_URI, values);
1702         }
1703     }
1704 }
1705