1 /* 2 * Copyright (C) 2010 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.contacts.common.model; 18 19 import android.content.ContentUris; 20 import android.net.Uri; 21 import android.provider.ContactsContract; 22 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 23 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 24 import android.provider.ContactsContract.Contacts; 25 import android.provider.ContactsContract.Data; 26 import android.provider.ContactsContract.DisplayNameSources; 27 import android.provider.ContactsContract.RawContacts; 28 import android.provider.ContactsContract.StatusUpdates; 29 import android.test.LoaderTestCase; 30 import android.test.suitebuilder.annotation.LargeTest; 31 32 import com.android.contacts.common.model.AccountTypeManager; 33 import com.android.contacts.common.test.mocks.ContactsMockContext; 34 import com.android.contacts.common.test.mocks.MockContentProvider; 35 import com.android.contacts.common.model.account.AccountType; 36 import com.android.contacts.common.model.account.AccountWithDataSet; 37 import com.android.contacts.common.model.account.BaseAccountType; 38 import com.android.contacts.common.testing.InjectedServices; 39 import com.android.contacts.common.test.mocks.MockAccountTypeManager; 40 import com.android.contacts.common.util.Constants; 41 42 import org.json.JSONException; 43 import org.json.JSONObject; 44 45 /** 46 * Runs ContactLoader tests for the the contact-detail and editor view. 47 */ 48 @LargeTest 49 public class ContactLoaderTest extends LoaderTestCase { 50 private static final long CONTACT_ID = 1; 51 private static final long RAW_CONTACT_ID = 11; 52 private static final long DATA_ID = 21; 53 private static final String LOOKUP_KEY = "aa%12%@!"; 54 55 private ContactsMockContext mMockContext; 56 private MockContentProvider mContactsProvider; 57 58 @Override setUp()59 protected void setUp() throws Exception { 60 super.setUp(); 61 mMockContext = new ContactsMockContext(getContext()); 62 mContactsProvider = mMockContext.getContactsProvider(); 63 64 InjectedServices services = new InjectedServices(); 65 AccountType accountType = new BaseAccountType() { 66 @Override 67 public boolean areContactsWritable() { 68 return false; 69 } 70 }; 71 accountType.accountType = "mockAccountType"; 72 73 AccountWithDataSet account = 74 new AccountWithDataSet("mockAccountName", "mockAccountType", null); 75 76 AccountTypeManager.setInstanceForTest( 77 new MockAccountTypeManager( 78 new AccountType[]{accountType}, new AccountWithDataSet[]{account})); 79 } 80 81 @Override tearDown()82 protected void tearDown() throws Exception { 83 mMockContext = null; 84 mContactsProvider = null; 85 super.tearDown(); 86 } 87 assertLoadContact(Uri uri)88 private Contact assertLoadContact(Uri uri) { 89 final ContactLoader loader = new ContactLoader(mMockContext, uri, true); 90 return getLoaderResultSynchronously(loader); 91 } 92 testNullUri()93 public void testNullUri() { 94 Contact result = assertLoadContact(null); 95 assertTrue(result.isError()); 96 } 97 testEmptyUri()98 public void testEmptyUri() { 99 Contact result = assertLoadContact(Uri.EMPTY); 100 assertTrue(result.isError()); 101 } 102 testInvalidUri()103 public void testInvalidUri() { 104 Contact result = assertLoadContact(Uri.parse("content://wtf")); 105 assertTrue(result.isError()); 106 } 107 testLoadContactWithContactIdUri()108 public void testLoadContactWithContactIdUri() { 109 // Use content Uris that only contain the ID 110 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 111 final Uri entityUri = Uri.withAppendedPath(baseUri, Contacts.Entity.CONTENT_DIRECTORY); 112 final Uri lookupUri = ContentUris.withAppendedId( 113 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 114 CONTACT_ID); 115 116 ContactQueries queries = new ContactQueries(); 117 mContactsProvider.expectTypeQuery(baseUri, Contacts.CONTENT_ITEM_TYPE); 118 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 119 120 Contact contact = assertLoadContact(baseUri); 121 122 assertEquals(CONTACT_ID, contact.getId()); 123 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 124 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 125 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 126 assertEquals(lookupUri, contact.getLookupUri()); 127 assertEquals(1, contact.getRawContacts().size()); 128 assertEquals(1, contact.getStatuses().size()); 129 mContactsProvider.verify(); 130 } 131 testLoadContactWithOldStyleUri()132 public void testLoadContactWithOldStyleUri() { 133 // Use content Uris that only contain the ID but use the format used in Donut 134 final Uri legacyUri = ContentUris.withAppendedId( 135 Uri.parse("content://contacts"), RAW_CONTACT_ID); 136 final Uri rawContactUri = ContentUris.withAppendedId( 137 RawContacts.CONTENT_URI, RAW_CONTACT_ID); 138 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 139 final Uri lookupUri = ContentUris.withAppendedId( 140 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 141 CONTACT_ID); 142 final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY); 143 144 ContactQueries queries = new ContactQueries(); 145 queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY); 146 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 147 148 Contact contact = assertLoadContact(legacyUri); 149 150 assertEquals(CONTACT_ID, contact.getId()); 151 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 152 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 153 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 154 assertEquals(lookupUri, contact.getLookupUri()); 155 assertEquals(1, contact.getRawContacts().size()); 156 assertEquals(1, contact.getStatuses().size()); 157 mContactsProvider.verify(); 158 } 159 testLoadContactWithRawContactIdUri()160 public void testLoadContactWithRawContactIdUri() { 161 // Use content Uris that only contain the ID but use the format used in Donut 162 final Uri rawContactUri = ContentUris.withAppendedId( 163 RawContacts.CONTENT_URI, RAW_CONTACT_ID); 164 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 165 final Uri lookupUri = ContentUris.withAppendedId( 166 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 167 CONTACT_ID); 168 final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY); 169 170 ContactQueries queries = new ContactQueries(); 171 mContactsProvider.expectTypeQuery(rawContactUri, RawContacts.CONTENT_ITEM_TYPE); 172 queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY); 173 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 174 175 Contact contact = assertLoadContact(rawContactUri); 176 177 assertEquals(CONTACT_ID, contact.getId()); 178 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 179 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 180 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 181 assertEquals(lookupUri, contact.getLookupUri()); 182 assertEquals(1, contact.getRawContacts().size()); 183 assertEquals(1, contact.getStatuses().size()); 184 mContactsProvider.verify(); 185 } 186 testLoadContactWithContactLookupUri()187 public void testLoadContactWithContactLookupUri() { 188 // Use lookup-style Uris that do not contain the Contact-ID 189 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 190 final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY); 191 final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, CONTACT_ID); 192 final Uri entityUri = Uri.withAppendedPath( 193 lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY); 194 195 ContactQueries queries = new ContactQueries(); 196 mContactsProvider.expectTypeQuery(lookupNoIdUri, Contacts.CONTENT_ITEM_TYPE); 197 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 198 199 Contact contact = assertLoadContact(lookupNoIdUri); 200 201 assertEquals(CONTACT_ID, contact.getId()); 202 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 203 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 204 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 205 assertEquals(lookupUri, contact.getLookupUri()); 206 assertEquals(1, contact.getRawContacts().size()); 207 assertEquals(1, contact.getStatuses().size()); 208 mContactsProvider.verify(); 209 } 210 testLoadContactWithContactLookupAndIdUri()211 public void testLoadContactWithContactLookupAndIdUri() { 212 // Use lookup-style Uris that also contain the Contact-ID 213 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 214 final Uri lookupUri = ContentUris.withAppendedId( 215 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 216 CONTACT_ID); 217 final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY); 218 219 ContactQueries queries = new ContactQueries(); 220 mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE); 221 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 222 223 Contact contact = assertLoadContact(lookupUri); 224 225 assertEquals(CONTACT_ID, contact.getId()); 226 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 227 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 228 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 229 assertEquals(lookupUri, contact.getLookupUri()); 230 assertEquals(1, contact.getRawContacts().size()); 231 assertEquals(1, contact.getStatuses().size()); 232 mContactsProvider.verify(); 233 } 234 testLoadContactWithContactLookupWithIncorrectIdUri()235 public void testLoadContactWithContactLookupWithIncorrectIdUri() { 236 // Use lookup-style Uris that contain incorrect Contact-ID 237 // (we want to ensure that still the correct contact is chosen) 238 final long wrongContactId = 2; 239 final long wrongRawContactId = 12; 240 241 final String wrongLookupKey = "ab%12%@!"; 242 final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID); 243 final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId); 244 final Uri lookupUri = ContentUris.withAppendedId( 245 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 246 CONTACT_ID); 247 final Uri lookupWithWrongIdUri = ContentUris.withAppendedId( 248 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY), 249 wrongContactId); 250 final Uri entityUri = Uri.withAppendedPath(lookupWithWrongIdUri, 251 Contacts.Entity.CONTENT_DIRECTORY); 252 253 ContactQueries queries = new ContactQueries(); 254 mContactsProvider.expectTypeQuery(lookupWithWrongIdUri, Contacts.CONTENT_ITEM_TYPE); 255 queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY); 256 257 Contact contact = assertLoadContact(lookupWithWrongIdUri); 258 259 assertEquals(CONTACT_ID, contact.getId()); 260 assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId()); 261 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 262 assertEquals(LOOKUP_KEY, contact.getLookupKey()); 263 assertEquals(lookupUri, contact.getLookupUri()); 264 assertEquals(1, contact.getRawContacts().size()); 265 assertEquals(1, contact.getStatuses().size()); 266 267 mContactsProvider.verify(); 268 } 269 testLoadContactReturnDirectoryContactWithoutDisplayName()270 public void testLoadContactReturnDirectoryContactWithoutDisplayName() throws JSONException { 271 // Use lookup-style Uri that contains encoded json object which encapsulates the 272 // directory contact. The test json object is: 273 // { 274 // display_name_source": 40, 275 // "vnd.android.cursor.item\/contact":{"email":{"data1":"test@google.com" }} 276 // } 277 JSONObject itemJson = new JSONObject(); 278 itemJson.put("email", new JSONObject().put("data1", "test@google.com")); 279 JSONObject json = new JSONObject(); 280 json.put(Contacts.NAME_RAW_CONTACT_ID, CONTACT_ID); 281 json.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 282 json.put(Contacts.CONTENT_ITEM_TYPE, itemJson); 283 284 final Uri lookupUri = Contacts.CONTENT_LOOKUP_URI.buildUpon() 285 .encodedFragment(json.toString()) 286 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, "1") 287 .appendPath(Constants.LOOKUP_URI_ENCODED).build(); 288 289 mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE); 290 Contact contact = assertLoadContact(lookupUri); 291 292 assertEquals(-1, contact.getId()); 293 assertEquals(-1, contact.getNameRawContactId()); 294 assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource()); 295 assertEquals("", contact.getDisplayName()); 296 assertEquals(lookupUri, contact.getLookupUri()); 297 assertEquals(1, contact.getRawContacts().size()); 298 mContactsProvider.verify(); 299 } 300 301 class ContactQueries { fetchAllData( Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup)302 public void fetchAllData( 303 Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup) { 304 mContactsProvider.expectQuery(baseUri) 305 .withProjection(new String[] { 306 Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE, 307 Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME, 308 Contacts.DISPLAY_NAME_ALTERNATIVE, Contacts.PHONETIC_NAME, 309 Contacts.PHOTO_ID, Contacts.STARRED, Contacts.CONTACT_PRESENCE, 310 Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_TIMESTAMP, 311 Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_LABEL, 312 313 Contacts.Entity.CONTACT_ID, 314 Contacts.Entity.RAW_CONTACT_ID, 315 316 RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, 317 RawContacts.DATA_SET, 318 RawContacts.DIRTY, RawContacts.VERSION, RawContacts.SOURCE_ID, 319 RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4, 320 RawContacts.DELETED, 321 322 Contacts.Entity.DATA_ID, 323 324 Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5, 325 Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, 326 Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15, 327 Data.SYNC1, Data.SYNC2, Data.SYNC3, Data.SYNC4, 328 Data.DATA_VERSION, Data.IS_PRIMARY, 329 Data.IS_SUPER_PRIMARY, Data.MIMETYPE, 330 331 GroupMembership.GROUP_SOURCE_ID, 332 333 Data.PRESENCE, Data.CHAT_CAPABILITY, 334 Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON, 335 Data.STATUS_LABEL, Data.STATUS_TIMESTAMP, 336 337 Contacts.PHOTO_URI, 338 339 Contacts.SEND_TO_VOICEMAIL, 340 Contacts.CUSTOM_RINGTONE, 341 Contacts.IS_USER_PROFILE, 342 343 Data.TIMES_USED, 344 Data.LAST_TIME_USED, 345 }) 346 .withSortOrder(Contacts.Entity.RAW_CONTACT_ID) 347 .returnRow( 348 rawContactId, 40, 349 "aa%12%@!", "John Doe", "Doe, John", "jdo", 350 0, 0, StatusUpdates.AVAILABLE, 351 "Having lunch", 0, 352 "mockPkg1", 10, 353 354 contactId, 355 rawContactId, 356 357 "mockAccountName", "mockAccountType", null, 358 0, 1, 0, 359 "sync1", "sync2", "sync3", "sync4", 360 0, 361 362 dataId, 363 364 "dat1", "dat2", "dat3", "dat4", "dat5", 365 "dat6", "dat7", "dat8", "dat9", "dat10", 366 "dat11", "dat12", "dat13", "dat14", "dat15", 367 "syn1", "syn2", "syn3", "syn4", 368 369 0, 0, 370 0, StructuredName.CONTENT_ITEM_TYPE, 371 372 "groupId", 373 374 StatusUpdates.INVISIBLE, null, 375 "Having dinner", "mockPkg3", 0, 376 20, 0, 377 378 "content:some.photo.uri", 379 380 0, 381 null, 382 0, 383 384 0, 385 0 386 ); 387 } 388 fetchLookupAndId(final Uri sourceUri, final long expectedContactId, final String expectedEncodedLookup)389 void fetchLookupAndId(final Uri sourceUri, final long expectedContactId, 390 final String expectedEncodedLookup) { 391 mContactsProvider.expectQuery(sourceUri) 392 .withProjection(Contacts.LOOKUP_KEY, Contacts._ID) 393 .returnRow(expectedEncodedLookup, expectedContactId); 394 } 395 fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri, final long expectedContactId, final String expectedEncodedLookup)396 void fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri, 397 final long expectedContactId, final String expectedEncodedLookup) { 398 // TODO: use a lighter query by joining rawcontacts with contacts in provider 399 // (See ContactContracts.java) 400 final Uri dataUri = Uri.withAppendedPath(rawContactUri, 401 RawContacts.Data.CONTENT_DIRECTORY); 402 mContactsProvider.expectQuery(dataUri) 403 .withProjection(RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY) 404 .returnRow(expectedContactId, expectedEncodedLookup); 405 } 406 } 407 } 408