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