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