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.TestUtils.cv; 20 import static com.android.providers.contacts.TestUtils.dumpCursor; 21 22 import static org.mockito.Mockito.eq; 23 import static org.mockito.Mockito.when; 24 25 import android.accounts.Account; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.ContentProviderOperation; 29 import android.content.ContentProviderResult; 30 import android.content.ContentResolver; 31 import android.content.ContentUris; 32 import android.content.ContentValues; 33 import android.content.Entity; 34 import android.content.EntityIterator; 35 import android.content.Intent; 36 import android.content.res.AssetFileDescriptor; 37 import android.database.Cursor; 38 import android.database.DatabaseUtils; 39 import android.database.MatrixCursor; 40 import android.database.sqlite.SQLiteDatabase; 41 import android.net.Uri; 42 import android.os.AsyncTask; 43 import android.os.Bundle; 44 import android.platform.test.annotations.RequiresFlagsEnabled; 45 import android.platform.test.flag.junit.CheckFlagsRule; 46 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 47 import android.provider.ContactsContract; 48 import android.provider.ContactsContract.AggregationExceptions; 49 import android.provider.ContactsContract.CommonDataKinds.Callable; 50 import android.provider.ContactsContract.CommonDataKinds.Contactables; 51 import android.provider.ContactsContract.CommonDataKinds.Email; 52 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 53 import android.provider.ContactsContract.CommonDataKinds.Im; 54 import android.provider.ContactsContract.CommonDataKinds.Organization; 55 import android.provider.ContactsContract.CommonDataKinds.Phone; 56 import android.provider.ContactsContract.CommonDataKinds.Photo; 57 import android.provider.ContactsContract.CommonDataKinds.SipAddress; 58 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 59 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 60 import android.provider.ContactsContract.Contacts; 61 import android.provider.ContactsContract.Data; 62 import android.provider.ContactsContract.DataUsageFeedback; 63 import android.provider.ContactsContract.Directory; 64 import android.provider.ContactsContract.DisplayNameSources; 65 import android.provider.ContactsContract.DisplayPhoto; 66 import android.provider.ContactsContract.FullNameStyle; 67 import android.provider.ContactsContract.Groups; 68 import android.provider.ContactsContract.PhoneLookup; 69 import android.provider.ContactsContract.PhoneticNameStyle; 70 import android.provider.ContactsContract.PinnedPositions; 71 import android.provider.ContactsContract.Profile; 72 import android.provider.ContactsContract.ProviderStatus; 73 import android.provider.ContactsContract.RawContacts; 74 import android.provider.ContactsContract.RawContactsEntity; 75 import android.provider.ContactsContract.SearchSnippets; 76 import android.provider.ContactsContract.Settings; 77 import android.provider.ContactsContract.StatusUpdates; 78 import android.provider.ContactsContract.StreamItemPhotos; 79 import android.provider.ContactsContract.StreamItems; 80 import android.provider.OpenableColumns; 81 import android.telecom.PhoneAccountHandle; 82 import android.telecom.TelecomManager; 83 import android.telephony.PhoneNumberUtils; 84 import android.telephony.SubscriptionInfo; 85 import android.test.MoreAsserts; 86 import android.text.TextUtils; 87 import android.util.ArraySet; 88 89 import androidx.test.annotation.UiThreadTest; 90 import androidx.test.runner.AndroidJUnit4; 91 92 import com.android.internal.util.ArrayUtils; 93 import com.android.providers.contacts.ContactsActor.AlteringUserContext; 94 import com.android.providers.contacts.ContactsActor.MockUserManager; 95 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns; 96 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; 97 import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; 98 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns; 99 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties; 100 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; 101 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; 102 import com.android.providers.contacts.ContactsDatabaseHelper.Tables; 103 import com.android.providers.contacts.flags.Flags; 104 import com.android.providers.contacts.tests.R; 105 import com.android.providers.contacts.testutil.CommonDatabaseUtils; 106 import com.android.providers.contacts.testutil.ContactUtil; 107 import com.android.providers.contacts.testutil.DataUtil; 108 import com.android.providers.contacts.testutil.DatabaseAsserts; 109 import com.android.providers.contacts.testutil.DeletedContactUtil; 110 import com.android.providers.contacts.testutil.RawContactUtil; 111 import com.android.providers.contacts.testutil.TestUtil; 112 import com.android.providers.contacts.util.NullContentProvider; 113 import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils; 114 import com.android.providers.contacts.util.UserUtils; 115 116 import com.google.android.collect.Lists; 117 import com.google.android.collect.Sets; 118 119 import org.junit.After; 120 import org.junit.Before; 121 import org.junit.Rule; 122 import org.junit.Test; 123 import org.junit.runner.RunWith; 124 125 import java.io.FileInputStream; 126 import java.io.IOException; 127 import java.io.OutputStream; 128 import java.text.Collator; 129 import java.util.ArrayList; 130 import java.util.Arrays; 131 import java.util.HashSet; 132 import java.util.List; 133 import java.util.Locale; 134 import java.util.Set; 135 136 /** 137 * Unit tests for {@link ContactsProvider2}. 138 * 139 * Run the test like this: 140 * <code> 141 adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \ 142 com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 143 * </code> 144 */ 145 @RunWith(AndroidJUnit4.class) 146 @UiThreadTest 147 public class ContactsProvider2Test extends BaseContactsProvider2Test { 148 149 private static final String TAG = ContactsProvider2Test.class.getSimpleName(); 150 151 private static final int MIN_MATCH = 7; 152 153 static final String TELEPHONY_PACKAGE = "com.android.phone"; 154 static final String TELEPHONY_CLASS 155 = "com.android.services.telephony.TelephonyConnectionService"; 156 static final String TEST_PHONE_ACCOUNT_HANDLE_SUB_ID = "666"; 157 static final int TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT = 666; 158 static final String TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1 = "T6E6S6T6I6C6C6I6D"; 159 static final String TEST_PHONE_ACCOUNT_HANDLE_ICC_ID2 = "T5E5S5T5I5C5C5I5D"; 160 static final String TEST_COMPONENT_NAME = "foo/bar"; 161 162 @Rule 163 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 164 165 private int mOldMinMatch1; 166 private int mOldMinMatch2; 167 168 ContactsDatabaseHelper mMockContactsDatabaseHelper; 169 private ContactsProvider2 mContactsProvider2; 170 private ContactsDatabaseHelper mDbHelper; 171 private BroadcastReceiver mBroadcastReceiver; 172 173 @Before 174 @Override setUp()175 public void setUp() throws Exception { 176 super.setUp(); 177 mContactsProvider2 = (ContactsProvider2) getProvider(); 178 mDbHelper = mContactsProvider2.getThreadActiveDatabaseHelperForTest(); 179 mBroadcastReceiver = mContactsProvider2.getBroadcastReceiverForTest(); 180 mOldMinMatch1 = PhoneNumberUtils.getMinMatchForTest(); 181 mOldMinMatch2 = mDbHelper.getMinMatchForTest(); 182 PhoneNumberUtils.setMinMatchForTest(MIN_MATCH); 183 mDbHelper.setMinMatchForTest(MIN_MATCH); 184 assertNotNull(mDbHelper); 185 } 186 187 @After 188 @Override tearDown()189 public void tearDown() throws Exception { 190 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 191 //final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest(); 192 PhoneNumberUtils.setMinMatchForTest(mOldMinMatch1); 193 mDbHelper.setMinMatchForTest(mOldMinMatch2); 194 super.tearDown(); 195 } 196 getMockContactsDatabaseHelper(String databaseNameForTesting)197 private ContactsDatabaseHelper getMockContactsDatabaseHelper(String databaseNameForTesting) { 198 ContactsDatabaseHelper contactsDatabaseHelper = new ContactsDatabaseHelper( 199 mTestContext, databaseNameForTesting, true, /* isTestInstance=*/ false); 200 SQLiteDatabase db = contactsDatabaseHelper.getWritableDatabase(); 201 db.execSQL("DELETE FROM " + ContactsDatabaseHelper.Tables.DATA); 202 { 203 final ContentValues values = new ContentValues(); 204 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 205 values.put(Data.RAW_CONTACT_ID, 6666); 206 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 207 PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME); 208 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 209 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1); 210 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 211 } 212 { 213 final ContentValues values = new ContentValues(); 214 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 215 values.put(Data.RAW_CONTACT_ID, 6666); 216 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 217 PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME); 218 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 219 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1); 220 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 221 } 222 { 223 final ContentValues values = new ContentValues(); 224 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 225 values.put(Data.RAW_CONTACT_ID, 6666); 226 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 227 PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME); 228 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 229 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1); 230 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 231 } 232 { 233 final ContentValues values = new ContentValues(); 234 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 235 values.put(Data.RAW_CONTACT_ID, 6666); 236 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 237 PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME); 238 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 239 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1); 240 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 241 } 242 { 243 final ContentValues values = new ContentValues(); 244 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 245 values.put(Data.RAW_CONTACT_ID, 6666); 246 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 247 PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME); 248 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 249 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "FAKE_ICCID"); 250 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 251 } 252 { 253 final ContentValues values = new ContentValues(); 254 values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666); 255 values.put(Data.RAW_CONTACT_ID, 6666); 256 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, TEST_COMPONENT_NAME); 257 values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1); 258 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "FAKE_ICCID"); 259 long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values); 260 } 261 return contactsDatabaseHelper; 262 } 263 264 @Test testPhoneAccountHandleMigrationSimEvent()265 public void testPhoneAccountHandleMigrationSimEvent() throws IOException { 266 ContactsDatabaseHelper originalContactsDatabaseHelper 267 = mContactsProvider2.getContactsDatabaseHelperForTest(); 268 269 // Mock SubscriptionManager 270 SubscriptionInfo subscriptionInfo = new SubscriptionInfo( 271 TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1, 272 1, "a", "b", 1, 1, "test", 1, null, null, null, null, false, null, null); 273 when(mSubscriptionManager.getActiveSubscriptionInfo( 274 eq(TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT))).thenReturn(subscriptionInfo); 275 276 // Mock ContactsDatabaseHelper 277 ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper( 278 "testContactsPhoneAccountHandleMigrationSimEvent.db"); 279 280 // Test setPhoneAccountMigrationStatusPending as false 281 PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils 282 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils(); 283 284 // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true 285 // and set for testing migration logic 286 phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true); 287 288 mContactsProvider2.setContactsDatabaseHelperForTest(contactsDatabaseHelper); 289 final SQLiteDatabase sqLiteDatabase = contactsDatabaseHelper.getReadableDatabase(); 290 291 // Check each entry in the Data has a new coloumn of 292 // Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING that has a value of 1 293 assertEquals(6 /** pending migration entries in the preconfigured file */, 294 DatabaseUtils.longForQuery(sqLiteDatabase, 295 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 296 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 297 + " = 1", null)); 298 299 // Prepare PhoneAccountHandle for the new sim event 300 PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle( 301 new ComponentName(TELEPHONY_PACKAGE, TELEPHONY_CLASS), 302 TEST_PHONE_ACCOUNT_HANDLE_SUB_ID); 303 Intent intent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED); 304 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 305 mBroadcastReceiver.onReceive(mTestContext, intent); 306 307 // Check four coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have migrated 308 assertEquals(4 /** entries in the preconfigured database file */, 309 DatabaseUtils.longForQuery(sqLiteDatabase, 310 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 311 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 312 + " = 0", null)); 313 // Check two coloumns 314 // of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have not migrated 315 assertEquals(2 /** pending migration entries after migration in the preconfigured file */, 316 DatabaseUtils.longForQuery(sqLiteDatabase, 317 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 318 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 319 + " = 1", null)); 320 321 mContactsProvider2.setContactsDatabaseHelperForTest(originalContactsDatabaseHelper); 322 } 323 324 @Test testPhoneAccountHandleMigrationInitiation()325 public void testPhoneAccountHandleMigrationInitiation() throws IOException { 326 ContactsDatabaseHelper originalContactsDatabaseHelper 327 = mContactsProvider2.getContactsDatabaseHelperForTest(); 328 329 // Mock SubscriptionManager 330 SubscriptionInfo subscriptionInfo = new SubscriptionInfo( 331 TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1, 332 1, "a", "b", 1, 1, "test", 1, null, null, null, null, false, null, null); 333 List<SubscriptionInfo> subscriptionInfoList = new ArrayList<>(); 334 subscriptionInfoList.add(subscriptionInfo); 335 when(mSubscriptionManager.getAllSubscriptionInfoList()).thenReturn(subscriptionInfoList); 336 337 // Mock ContactsDatabaseHelper 338 ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper( 339 "testContactsPhoneAccountHandleMigrationInitiation.db"); 340 341 // Test setPhoneAccountMigrationStatusPending as false 342 PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils 343 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils(); 344 345 // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true 346 // and set for testing migration logic 347 phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true); 348 349 mContactsProvider2.setContactsDatabaseHelperForTest(contactsDatabaseHelper); 350 final SQLiteDatabase sqLiteDatabase = contactsDatabaseHelper.getReadableDatabase(); 351 352 // Check each entry in the Data has a new coloumn of 353 // Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING that has a value of 1 354 assertEquals(6, DatabaseUtils.longForQuery(sqLiteDatabase, 355 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 356 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 357 + " = 1", null)); 358 359 // Prepare Task for BACKGROUND_TASK_MIGRATE_PHONE_ACCOUNT_HANDLES 360 mContactsProvider2.performBackgroundTask( 361 mContactsProvider2.BACKGROUND_TASK_MIGRATE_PHONE_ACCOUNT_HANDLES, null); 362 363 // Check four coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have migrated 364 assertEquals(4, DatabaseUtils.longForQuery(sqLiteDatabase, 365 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 366 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 367 + " = 0", null)); 368 // Check two coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have not migrated 369 assertEquals(2, DatabaseUtils.longForQuery(sqLiteDatabase, 370 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA 371 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING 372 + " = 1", null)); 373 374 // Verify the pending status of phone account migration. 375 assertTrue(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending()); 376 377 mContactsProvider2.setContactsDatabaseHelperForTest(originalContactsDatabaseHelper); 378 } 379 380 @Test testPhoneAccountHandleMigrationPendingStatus()381 public void testPhoneAccountHandleMigrationPendingStatus() { 382 // Mock ContactsDatabaseHelper 383 ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper( 384 "testPhoneAccountHandleMigrationPendingStatus.db"); 385 386 // Test setPhoneAccountMigrationStatusPending as false 387 PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils 388 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils(); 389 phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(false); 390 assertFalse(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending()); 391 392 // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true 393 // and set for testing migration logic 394 phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true); 395 assertTrue(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending()); 396 } 397 398 @Test testConvertEnterpriseUriWithEnterpriseDirectoryToLocalUri()399 public void testConvertEnterpriseUriWithEnterpriseDirectoryToLocalUri() { 400 String phoneNumber = "886"; 401 String directory = String.valueOf(Directory.ENTERPRISE_DEFAULT); 402 boolean isSip = true; 403 Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber) 404 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory) 405 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 406 String.valueOf(isSip)).build(); 407 Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI); 408 Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber) 409 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 410 String.valueOf(Directory.DEFAULT)) 411 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 412 String.valueOf(isSip)).build(); 413 assertUriEquals(expectedUri, localUri); 414 } 415 416 @Test testConvertEnterpriseUriWithPersonalDirectoryToLocalUri()417 public void testConvertEnterpriseUriWithPersonalDirectoryToLocalUri() { 418 String phoneNumber = "886"; 419 String directory = String.valueOf(Directory.DEFAULT); 420 boolean isSip = true; 421 Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber) 422 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory) 423 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 424 String.valueOf(isSip)).build(); 425 Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI); 426 Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber) 427 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 428 String.valueOf(Directory.DEFAULT)) 429 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 430 String.valueOf(isSip)).build(); 431 assertUriEquals(expectedUri, localUri); 432 } 433 434 @Test testConvertEnterpriseUriWithoutDirectoryToLocalUri()435 public void testConvertEnterpriseUriWithoutDirectoryToLocalUri() { 436 String phoneNumber = "886"; 437 boolean isSip = true; 438 Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber) 439 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 440 String.valueOf(isSip)).build(); 441 Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI); 442 Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber) 443 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, 444 String.valueOf(isSip)).build(); 445 assertUriEquals(expectedUri, localUri); 446 } 447 448 @Test testContactsProjection()449 public void testContactsProjection() { 450 assertProjection(Contacts.CONTENT_URI, new String[]{ 451 Contacts._ID, 452 Contacts.DISPLAY_NAME_PRIMARY, 453 Contacts.DISPLAY_NAME_ALTERNATIVE, 454 Contacts.DISPLAY_NAME_SOURCE, 455 Contacts.PHONETIC_NAME, 456 Contacts.PHONETIC_NAME_STYLE, 457 Contacts.SORT_KEY_PRIMARY, 458 Contacts.SORT_KEY_ALTERNATIVE, 459 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 460 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 461 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 462 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 463 Contacts.LAST_TIME_CONTACTED, 464 Contacts.TIMES_CONTACTED, 465 Contacts.STARRED, 466 Contacts.PINNED, 467 Contacts.IN_DEFAULT_DIRECTORY, 468 Contacts.IN_VISIBLE_GROUP, 469 Contacts.PHOTO_ID, 470 Contacts.PHOTO_FILE_ID, 471 Contacts.PHOTO_URI, 472 Contacts.PHOTO_THUMBNAIL_URI, 473 Contacts.CUSTOM_RINGTONE, 474 Contacts.HAS_PHONE_NUMBER, 475 Contacts.SEND_TO_VOICEMAIL, 476 Contacts.IS_USER_PROFILE, 477 Contacts.LOOKUP_KEY, 478 Contacts.NAME_RAW_CONTACT_ID, 479 Contacts.CONTACT_PRESENCE, 480 Contacts.CONTACT_CHAT_CAPABILITY, 481 Contacts.CONTACT_STATUS, 482 Contacts.CONTACT_STATUS_TIMESTAMP, 483 Contacts.CONTACT_STATUS_RES_PACKAGE, 484 Contacts.CONTACT_STATUS_LABEL, 485 Contacts.CONTACT_STATUS_ICON, 486 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP 487 }); 488 } 489 490 @Test testContactsStrequentProjection()491 public void testContactsStrequentProjection() { 492 assertProjection(Contacts.CONTENT_STREQUENT_URI, new String[]{ 493 Contacts._ID, 494 Contacts.DISPLAY_NAME_PRIMARY, 495 Contacts.DISPLAY_NAME_ALTERNATIVE, 496 Contacts.DISPLAY_NAME_SOURCE, 497 Contacts.PHONETIC_NAME, 498 Contacts.PHONETIC_NAME_STYLE, 499 Contacts.SORT_KEY_PRIMARY, 500 Contacts.SORT_KEY_ALTERNATIVE, 501 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 502 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 503 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 504 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 505 Contacts.LAST_TIME_CONTACTED, 506 Contacts.TIMES_CONTACTED, 507 Contacts.STARRED, 508 Contacts.PINNED, 509 Contacts.IN_DEFAULT_DIRECTORY, 510 Contacts.IN_VISIBLE_GROUP, 511 Contacts.PHOTO_ID, 512 Contacts.PHOTO_FILE_ID, 513 Contacts.PHOTO_URI, 514 Contacts.PHOTO_THUMBNAIL_URI, 515 Contacts.CUSTOM_RINGTONE, 516 Contacts.HAS_PHONE_NUMBER, 517 Contacts.SEND_TO_VOICEMAIL, 518 Contacts.IS_USER_PROFILE, 519 Contacts.LOOKUP_KEY, 520 Contacts.NAME_RAW_CONTACT_ID, 521 Contacts.CONTACT_PRESENCE, 522 Contacts.CONTACT_CHAT_CAPABILITY, 523 Contacts.CONTACT_STATUS, 524 Contacts.CONTACT_STATUS_TIMESTAMP, 525 Contacts.CONTACT_STATUS_RES_PACKAGE, 526 Contacts.CONTACT_STATUS_LABEL, 527 Contacts.CONTACT_STATUS_ICON, 528 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 529 DataUsageStatColumns.LR_TIMES_USED, 530 DataUsageStatColumns.LR_LAST_TIME_USED, 531 }); 532 } 533 534 @Test testContactsStrequentPhoneOnlyProjection()535 public void testContactsStrequentPhoneOnlyProjection() { 536 assertProjection(Contacts.CONTENT_STREQUENT_URI.buildUpon() 537 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build(), 538 new String[] { 539 Contacts._ID, 540 Contacts.DISPLAY_NAME_PRIMARY, 541 Contacts.DISPLAY_NAME_ALTERNATIVE, 542 Contacts.DISPLAY_NAME_SOURCE, 543 Contacts.PHONETIC_NAME, 544 Contacts.PHONETIC_NAME_STYLE, 545 Contacts.SORT_KEY_PRIMARY, 546 Contacts.SORT_KEY_ALTERNATIVE, 547 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 548 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 549 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 550 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 551 Contacts.LAST_TIME_CONTACTED, 552 Contacts.TIMES_CONTACTED, 553 Contacts.STARRED, 554 Contacts.PINNED, 555 Contacts.IN_DEFAULT_DIRECTORY, 556 Contacts.IN_VISIBLE_GROUP, 557 Contacts.PHOTO_ID, 558 Contacts.PHOTO_FILE_ID, 559 Contacts.PHOTO_URI, 560 Contacts.PHOTO_THUMBNAIL_URI, 561 Contacts.CUSTOM_RINGTONE, 562 Contacts.HAS_PHONE_NUMBER, 563 Contacts.SEND_TO_VOICEMAIL, 564 Contacts.IS_USER_PROFILE, 565 Contacts.LOOKUP_KEY, 566 Contacts.NAME_RAW_CONTACT_ID, 567 Contacts.CONTACT_PRESENCE, 568 Contacts.CONTACT_CHAT_CAPABILITY, 569 Contacts.CONTACT_STATUS, 570 Contacts.CONTACT_STATUS_TIMESTAMP, 571 Contacts.CONTACT_STATUS_RES_PACKAGE, 572 Contacts.CONTACT_STATUS_LABEL, 573 Contacts.CONTACT_STATUS_ICON, 574 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 575 DataUsageStatColumns.LR_TIMES_USED, 576 DataUsageStatColumns.LR_LAST_TIME_USED, 577 Phone.NUMBER, 578 Phone.TYPE, 579 Phone.LABEL, 580 Phone.IS_SUPER_PRIMARY, 581 Phone.CONTACT_ID 582 }); 583 } 584 585 @Test testContactsWithSnippetProjection()586 public void testContactsWithSnippetProjection() { 587 assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(), 588 new String[]{ 589 Contacts._ID, 590 Contacts.DISPLAY_NAME_PRIMARY, 591 Contacts.DISPLAY_NAME_ALTERNATIVE, 592 Contacts.DISPLAY_NAME_SOURCE, 593 Contacts.PHONETIC_NAME, 594 Contacts.PHONETIC_NAME_STYLE, 595 Contacts.SORT_KEY_PRIMARY, 596 Contacts.SORT_KEY_ALTERNATIVE, 597 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 598 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 599 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 600 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 601 Contacts.LAST_TIME_CONTACTED, 602 Contacts.TIMES_CONTACTED, 603 Contacts.STARRED, 604 Contacts.PINNED, 605 Contacts.IN_DEFAULT_DIRECTORY, 606 Contacts.IN_VISIBLE_GROUP, 607 Contacts.PHOTO_ID, 608 Contacts.PHOTO_FILE_ID, 609 Contacts.PHOTO_URI, 610 Contacts.PHOTO_THUMBNAIL_URI, 611 Contacts.CUSTOM_RINGTONE, 612 Contacts.HAS_PHONE_NUMBER, 613 Contacts.SEND_TO_VOICEMAIL, 614 Contacts.IS_USER_PROFILE, 615 Contacts.LOOKUP_KEY, 616 Contacts.NAME_RAW_CONTACT_ID, 617 Contacts.CONTACT_PRESENCE, 618 Contacts.CONTACT_CHAT_CAPABILITY, 619 Contacts.CONTACT_STATUS, 620 Contacts.CONTACT_STATUS_TIMESTAMP, 621 Contacts.CONTACT_STATUS_RES_PACKAGE, 622 Contacts.CONTACT_STATUS_LABEL, 623 Contacts.CONTACT_STATUS_ICON, 624 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 625 SearchSnippets.SNIPPET, 626 }); 627 } 628 629 @Test testRawContactsProjection()630 public void testRawContactsProjection() { 631 assertProjection(RawContacts.CONTENT_URI, new String[]{ 632 RawContacts._ID, 633 RawContacts.CONTACT_ID, 634 RawContacts.ACCOUNT_NAME, 635 RawContacts.ACCOUNT_TYPE, 636 RawContacts.DATA_SET, 637 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 638 RawContacts.SOURCE_ID, 639 RawContacts.BACKUP_ID, 640 RawContacts.VERSION, 641 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 642 RawContacts.DIRTY, 643 RawContacts.METADATA_DIRTY, 644 RawContacts.DELETED, 645 RawContacts.DISPLAY_NAME_PRIMARY, 646 RawContacts.DISPLAY_NAME_ALTERNATIVE, 647 RawContacts.DISPLAY_NAME_SOURCE, 648 RawContacts.PHONETIC_NAME, 649 RawContacts.PHONETIC_NAME_STYLE, 650 RawContacts.SORT_KEY_PRIMARY, 651 RawContacts.SORT_KEY_ALTERNATIVE, 652 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, 653 RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 654 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 655 RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 656 RawContacts.TIMES_CONTACTED, 657 RawContacts.LAST_TIME_CONTACTED, 658 RawContacts.CUSTOM_RINGTONE, 659 RawContacts.SEND_TO_VOICEMAIL, 660 RawContacts.STARRED, 661 RawContacts.PINNED, 662 RawContacts.AGGREGATION_MODE, 663 RawContacts.SYNC1, 664 RawContacts.SYNC2, 665 RawContacts.SYNC3, 666 RawContacts.SYNC4, 667 }); 668 } 669 670 @Test testDataProjection()671 public void testDataProjection() { 672 assertProjection(Data.CONTENT_URI, new String[]{ 673 Data._ID, 674 Data.RAW_CONTACT_ID, 675 Data.HASH_ID, 676 Data.DATA_VERSION, 677 Data.IS_PRIMARY, 678 Data.IS_SUPER_PRIMARY, 679 Data.RES_PACKAGE, 680 Data.MIMETYPE, 681 Data.DATA1, 682 Data.DATA2, 683 Data.DATA3, 684 Data.DATA4, 685 Data.DATA5, 686 Data.DATA6, 687 Data.DATA7, 688 Data.DATA8, 689 Data.DATA9, 690 Data.DATA10, 691 Data.DATA11, 692 Data.DATA12, 693 Data.DATA13, 694 Data.DATA14, 695 Data.DATA15, 696 Data.CARRIER_PRESENCE, 697 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 698 Data.PREFERRED_PHONE_ACCOUNT_ID, 699 Data.SYNC1, 700 Data.SYNC2, 701 Data.SYNC3, 702 Data.SYNC4, 703 Data.CONTACT_ID, 704 Data.PRESENCE, 705 Data.CHAT_CAPABILITY, 706 Data.STATUS, 707 Data.STATUS_TIMESTAMP, 708 Data.STATUS_RES_PACKAGE, 709 Data.STATUS_LABEL, 710 Data.STATUS_ICON, 711 Data.TIMES_USED, 712 Data.LAST_TIME_USED, 713 RawContacts.ACCOUNT_NAME, 714 RawContacts.ACCOUNT_TYPE, 715 RawContacts.DATA_SET, 716 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 717 RawContacts.SOURCE_ID, 718 RawContacts.BACKUP_ID, 719 RawContacts.VERSION, 720 RawContacts.DIRTY, 721 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 722 Contacts._ID, 723 Contacts.DISPLAY_NAME_PRIMARY, 724 Contacts.DISPLAY_NAME_ALTERNATIVE, 725 Contacts.DISPLAY_NAME_SOURCE, 726 Contacts.PHONETIC_NAME, 727 Contacts.PHONETIC_NAME_STYLE, 728 Contacts.SORT_KEY_PRIMARY, 729 Contacts.SORT_KEY_ALTERNATIVE, 730 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 731 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 732 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 733 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 734 Contacts.LAST_TIME_CONTACTED, 735 Contacts.TIMES_CONTACTED, 736 Contacts.STARRED, 737 Contacts.PINNED, 738 Contacts.IN_DEFAULT_DIRECTORY, 739 Contacts.IN_VISIBLE_GROUP, 740 Contacts.PHOTO_ID, 741 Contacts.PHOTO_FILE_ID, 742 Contacts.PHOTO_URI, 743 Contacts.PHOTO_THUMBNAIL_URI, 744 Contacts.CUSTOM_RINGTONE, 745 Contacts.SEND_TO_VOICEMAIL, 746 Contacts.LOOKUP_KEY, 747 Contacts.NAME_RAW_CONTACT_ID, 748 Contacts.HAS_PHONE_NUMBER, 749 Contacts.CONTACT_PRESENCE, 750 Contacts.CONTACT_CHAT_CAPABILITY, 751 Contacts.CONTACT_STATUS, 752 Contacts.CONTACT_STATUS_TIMESTAMP, 753 Contacts.CONTACT_STATUS_RES_PACKAGE, 754 Contacts.CONTACT_STATUS_LABEL, 755 Contacts.CONTACT_STATUS_ICON, 756 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 757 GroupMembership.GROUP_SOURCE_ID, 758 }); 759 } 760 761 @Test testDistinctDataProjection()762 public void testDistinctDataProjection() { 763 assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 764 new String[]{ 765 Data._ID, 766 Data.HASH_ID, 767 Data.DATA_VERSION, 768 Data.IS_PRIMARY, 769 Data.IS_SUPER_PRIMARY, 770 Data.RES_PACKAGE, 771 Data.MIMETYPE, 772 Data.DATA1, 773 Data.DATA2, 774 Data.DATA3, 775 Data.DATA4, 776 Data.DATA5, 777 Data.DATA6, 778 Data.DATA7, 779 Data.DATA8, 780 Data.DATA9, 781 Data.DATA10, 782 Data.DATA11, 783 Data.DATA12, 784 Data.DATA13, 785 Data.DATA14, 786 Data.DATA15, 787 Data.CARRIER_PRESENCE, 788 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 789 Data.PREFERRED_PHONE_ACCOUNT_ID, 790 Data.SYNC1, 791 Data.SYNC2, 792 Data.SYNC3, 793 Data.SYNC4, 794 Data.CONTACT_ID, 795 Data.PRESENCE, 796 Data.CHAT_CAPABILITY, 797 Data.STATUS, 798 Data.STATUS_TIMESTAMP, 799 Data.STATUS_RES_PACKAGE, 800 Data.STATUS_LABEL, 801 Data.STATUS_ICON, 802 Data.TIMES_USED, 803 Data.LAST_TIME_USED, 804 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 805 Contacts._ID, 806 Contacts.DISPLAY_NAME_PRIMARY, 807 Contacts.DISPLAY_NAME_ALTERNATIVE, 808 Contacts.DISPLAY_NAME_SOURCE, 809 Contacts.PHONETIC_NAME, 810 Contacts.PHONETIC_NAME_STYLE, 811 Contacts.SORT_KEY_PRIMARY, 812 Contacts.SORT_KEY_ALTERNATIVE, 813 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 814 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 815 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 816 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 817 Contacts.LAST_TIME_CONTACTED, 818 Contacts.TIMES_CONTACTED, 819 Contacts.STARRED, 820 Contacts.PINNED, 821 Contacts.IN_DEFAULT_DIRECTORY, 822 Contacts.IN_VISIBLE_GROUP, 823 Contacts.PHOTO_ID, 824 Contacts.PHOTO_FILE_ID, 825 Contacts.PHOTO_URI, 826 Contacts.PHOTO_THUMBNAIL_URI, 827 Contacts.HAS_PHONE_NUMBER, 828 Contacts.CUSTOM_RINGTONE, 829 Contacts.SEND_TO_VOICEMAIL, 830 Contacts.LOOKUP_KEY, 831 Contacts.CONTACT_PRESENCE, 832 Contacts.CONTACT_CHAT_CAPABILITY, 833 Contacts.CONTACT_STATUS, 834 Contacts.CONTACT_STATUS_TIMESTAMP, 835 Contacts.CONTACT_STATUS_RES_PACKAGE, 836 Contacts.CONTACT_STATUS_LABEL, 837 Contacts.CONTACT_STATUS_ICON, 838 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 839 GroupMembership.GROUP_SOURCE_ID, 840 }); 841 } 842 843 @Test testEntityProjection()844 public void testEntityProjection() { 845 assertProjection( 846 Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0), 847 Contacts.Entity.CONTENT_DIRECTORY), 848 new String[]{ 849 Contacts.Entity._ID, 850 Contacts.Entity.DATA_ID, 851 Contacts.Entity.RAW_CONTACT_ID, 852 Data.DATA_VERSION, 853 Data.IS_PRIMARY, 854 Data.IS_SUPER_PRIMARY, 855 Data.RES_PACKAGE, 856 Data.MIMETYPE, 857 Data.DATA1, 858 Data.DATA2, 859 Data.DATA3, 860 Data.DATA4, 861 Data.DATA5, 862 Data.DATA6, 863 Data.DATA7, 864 Data.DATA8, 865 Data.DATA9, 866 Data.DATA10, 867 Data.DATA11, 868 Data.DATA12, 869 Data.DATA13, 870 Data.DATA14, 871 Data.DATA15, 872 Data.CARRIER_PRESENCE, 873 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 874 Data.PREFERRED_PHONE_ACCOUNT_ID, 875 Data.SYNC1, 876 Data.SYNC2, 877 Data.SYNC3, 878 Data.SYNC4, 879 Data.CONTACT_ID, 880 Data.PRESENCE, 881 Data.CHAT_CAPABILITY, 882 Data.STATUS, 883 Data.STATUS_TIMESTAMP, 884 Data.STATUS_RES_PACKAGE, 885 Data.STATUS_LABEL, 886 Data.STATUS_ICON, 887 RawContacts.ACCOUNT_NAME, 888 RawContacts.ACCOUNT_TYPE, 889 RawContacts.DATA_SET, 890 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 891 RawContacts.SOURCE_ID, 892 RawContacts.BACKUP_ID, 893 RawContacts.VERSION, 894 RawContacts.DELETED, 895 RawContacts.DIRTY, 896 RawContacts.SYNC1, 897 RawContacts.SYNC2, 898 RawContacts.SYNC3, 899 RawContacts.SYNC4, 900 Contacts._ID, 901 Contacts.DISPLAY_NAME_PRIMARY, 902 Contacts.DISPLAY_NAME_ALTERNATIVE, 903 Contacts.DISPLAY_NAME_SOURCE, 904 Contacts.PHONETIC_NAME, 905 Contacts.PHONETIC_NAME_STYLE, 906 Contacts.SORT_KEY_PRIMARY, 907 Contacts.SORT_KEY_ALTERNATIVE, 908 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 909 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 910 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 911 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 912 Contacts.LAST_TIME_CONTACTED, 913 Contacts.TIMES_CONTACTED, 914 Contacts.STARRED, 915 Contacts.PINNED, 916 Contacts.IN_DEFAULT_DIRECTORY, 917 Contacts.IN_VISIBLE_GROUP, 918 Contacts.PHOTO_ID, 919 Contacts.PHOTO_FILE_ID, 920 Contacts.PHOTO_URI, 921 Contacts.PHOTO_THUMBNAIL_URI, 922 Contacts.CUSTOM_RINGTONE, 923 Contacts.SEND_TO_VOICEMAIL, 924 Contacts.IS_USER_PROFILE, 925 Contacts.LOOKUP_KEY, 926 Contacts.NAME_RAW_CONTACT_ID, 927 Contacts.HAS_PHONE_NUMBER, 928 Contacts.CONTACT_PRESENCE, 929 Contacts.CONTACT_CHAT_CAPABILITY, 930 Contacts.CONTACT_STATUS, 931 Contacts.CONTACT_STATUS_TIMESTAMP, 932 Contacts.CONTACT_STATUS_RES_PACKAGE, 933 Contacts.CONTACT_STATUS_LABEL, 934 Contacts.CONTACT_STATUS_ICON, 935 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 936 GroupMembership.GROUP_SOURCE_ID, 937 Contacts.Entity.TIMES_USED, 938 Contacts.Entity.LAST_TIME_USED, 939 }); 940 } 941 942 @Test testRawEntityProjection()943 public void testRawEntityProjection() { 944 assertProjection(RawContactsEntity.CONTENT_URI, new String[]{ 945 RawContacts.Entity.DATA_ID, 946 RawContacts._ID, 947 RawContacts.CONTACT_ID, 948 RawContacts.ACCOUNT_NAME, 949 RawContacts.ACCOUNT_TYPE, 950 RawContacts.DATA_SET, 951 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 952 RawContacts.SOURCE_ID, 953 RawContacts.BACKUP_ID, 954 RawContacts.VERSION, 955 RawContacts.DIRTY, 956 RawContacts.DELETED, 957 RawContacts.SYNC1, 958 RawContacts.SYNC2, 959 RawContacts.SYNC3, 960 RawContacts.SYNC4, 961 RawContacts.STARRED, 962 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 963 Data.DATA_VERSION, 964 Data.IS_PRIMARY, 965 Data.IS_SUPER_PRIMARY, 966 Data.RES_PACKAGE, 967 Data.MIMETYPE, 968 Data.DATA1, 969 Data.DATA2, 970 Data.DATA3, 971 Data.DATA4, 972 Data.DATA5, 973 Data.DATA6, 974 Data.DATA7, 975 Data.DATA8, 976 Data.DATA9, 977 Data.DATA10, 978 Data.DATA11, 979 Data.DATA12, 980 Data.DATA13, 981 Data.DATA14, 982 Data.DATA15, 983 Data.CARRIER_PRESENCE, 984 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 985 Data.PREFERRED_PHONE_ACCOUNT_ID, 986 Data.SYNC1, 987 Data.SYNC2, 988 Data.SYNC3, 989 Data.SYNC4, 990 GroupMembership.GROUP_SOURCE_ID, 991 }); 992 } 993 994 @Test testPhoneLookupProjection()995 public void testPhoneLookupProjection() { 996 assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 997 new String[]{ 998 PhoneLookup._ID, 999 PhoneLookup.CONTACT_ID, 1000 PhoneLookup.DATA_ID, 1001 PhoneLookup.LOOKUP_KEY, 1002 PhoneLookup.DISPLAY_NAME_SOURCE, 1003 PhoneLookup.DISPLAY_NAME, 1004 PhoneLookup.DISPLAY_NAME_ALTERNATIVE, 1005 PhoneLookup.PHONETIC_NAME, 1006 PhoneLookup.PHONETIC_NAME_STYLE, 1007 PhoneLookup.SORT_KEY_PRIMARY, 1008 PhoneLookup.SORT_KEY_ALTERNATIVE, 1009 PhoneLookup.LAST_TIME_CONTACTED, 1010 PhoneLookup.TIMES_CONTACTED, 1011 PhoneLookup.STARRED, 1012 PhoneLookup.IN_DEFAULT_DIRECTORY, 1013 PhoneLookup.IN_VISIBLE_GROUP, 1014 PhoneLookup.PHOTO_FILE_ID, 1015 PhoneLookup.PHOTO_ID, 1016 PhoneLookup.PHOTO_URI, 1017 PhoneLookup.PHOTO_THUMBNAIL_URI, 1018 PhoneLookup.CUSTOM_RINGTONE, 1019 PhoneLookup.HAS_PHONE_NUMBER, 1020 PhoneLookup.SEND_TO_VOICEMAIL, 1021 PhoneLookup.NUMBER, 1022 PhoneLookup.TYPE, 1023 PhoneLookup.LABEL, 1024 PhoneLookup.NORMALIZED_NUMBER, 1025 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 1026 Data.PREFERRED_PHONE_ACCOUNT_ID, 1027 }); 1028 } 1029 1030 @Test testPhoneLookupEnterpriseProjection()1031 public void testPhoneLookupEnterpriseProjection() { 1032 assertProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI 1033 .buildUpon().appendPath("123").build(), 1034 new String[]{ 1035 PhoneLookup._ID, 1036 PhoneLookup.CONTACT_ID, 1037 PhoneLookup.DATA_ID, 1038 PhoneLookup.LOOKUP_KEY, 1039 PhoneLookup.DISPLAY_NAME_SOURCE, 1040 PhoneLookup.DISPLAY_NAME, 1041 PhoneLookup.DISPLAY_NAME_ALTERNATIVE, 1042 PhoneLookup.PHONETIC_NAME, 1043 PhoneLookup.PHONETIC_NAME_STYLE, 1044 PhoneLookup.SORT_KEY_PRIMARY, 1045 PhoneLookup.SORT_KEY_ALTERNATIVE, 1046 PhoneLookup.LAST_TIME_CONTACTED, 1047 PhoneLookup.TIMES_CONTACTED, 1048 PhoneLookup.STARRED, 1049 PhoneLookup.IN_DEFAULT_DIRECTORY, 1050 PhoneLookup.IN_VISIBLE_GROUP, 1051 PhoneLookup.PHOTO_FILE_ID, 1052 PhoneLookup.PHOTO_ID, 1053 PhoneLookup.PHOTO_URI, 1054 PhoneLookup.PHOTO_THUMBNAIL_URI, 1055 PhoneLookup.CUSTOM_RINGTONE, 1056 PhoneLookup.HAS_PHONE_NUMBER, 1057 PhoneLookup.SEND_TO_VOICEMAIL, 1058 PhoneLookup.NUMBER, 1059 PhoneLookup.TYPE, 1060 PhoneLookup.LABEL, 1061 PhoneLookup.NORMALIZED_NUMBER, 1062 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 1063 Data.PREFERRED_PHONE_ACCOUNT_ID, 1064 }); 1065 } 1066 1067 @Test testSipPhoneLookupProjection()1068 public void testSipPhoneLookupProjection() { 1069 assertContainProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123") 1070 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1") 1071 .build(), 1072 new String[] { 1073 PhoneLookup._ID, 1074 PhoneLookup.CONTACT_ID, 1075 PhoneLookup.DATA_ID, 1076 PhoneLookup.LOOKUP_KEY, 1077 PhoneLookup.DISPLAY_NAME, 1078 PhoneLookup.LAST_TIME_CONTACTED, 1079 PhoneLookup.TIMES_CONTACTED, 1080 PhoneLookup.STARRED, 1081 PhoneLookup.IN_DEFAULT_DIRECTORY, 1082 PhoneLookup.IN_VISIBLE_GROUP, 1083 PhoneLookup.PHOTO_FILE_ID, 1084 PhoneLookup.PHOTO_ID, 1085 PhoneLookup.PHOTO_URI, 1086 PhoneLookup.PHOTO_THUMBNAIL_URI, 1087 PhoneLookup.CUSTOM_RINGTONE, 1088 PhoneLookup.HAS_PHONE_NUMBER, 1089 PhoneLookup.SEND_TO_VOICEMAIL, 1090 PhoneLookup.NUMBER, 1091 PhoneLookup.TYPE, 1092 PhoneLookup.LABEL, 1093 PhoneLookup.NORMALIZED_NUMBER, 1094 }); 1095 } 1096 1097 @Test testSipPhoneLookupEnterpriseProjection()1098 public void testSipPhoneLookupEnterpriseProjection() { 1099 assertContainProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI 1100 .buildUpon() 1101 .appendPath("123") 1102 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1") 1103 .build(), 1104 new String[] { 1105 PhoneLookup._ID, 1106 PhoneLookup.CONTACT_ID, 1107 PhoneLookup.DATA_ID, 1108 PhoneLookup.LOOKUP_KEY, 1109 PhoneLookup.DISPLAY_NAME, 1110 PhoneLookup.LAST_TIME_CONTACTED, 1111 PhoneLookup.TIMES_CONTACTED, 1112 PhoneLookup.STARRED, 1113 PhoneLookup.IN_DEFAULT_DIRECTORY, 1114 PhoneLookup.IN_VISIBLE_GROUP, 1115 PhoneLookup.PHOTO_FILE_ID, 1116 PhoneLookup.PHOTO_ID, 1117 PhoneLookup.PHOTO_URI, 1118 PhoneLookup.PHOTO_THUMBNAIL_URI, 1119 PhoneLookup.CUSTOM_RINGTONE, 1120 PhoneLookup.HAS_PHONE_NUMBER, 1121 PhoneLookup.SEND_TO_VOICEMAIL, 1122 PhoneLookup.NUMBER, 1123 PhoneLookup.TYPE, 1124 PhoneLookup.LABEL, 1125 PhoneLookup.NORMALIZED_NUMBER, 1126 }); 1127 } 1128 1129 @Test testGroupsProjection()1130 public void testGroupsProjection() { 1131 assertProjection(Groups.CONTENT_URI, new String[]{ 1132 Groups._ID, 1133 Groups.ACCOUNT_NAME, 1134 Groups.ACCOUNT_TYPE, 1135 Groups.DATA_SET, 1136 Groups.ACCOUNT_TYPE_AND_DATA_SET, 1137 Groups.SOURCE_ID, 1138 Groups.DIRTY, 1139 Groups.VERSION, 1140 Groups.RES_PACKAGE, 1141 Groups.TITLE, 1142 Groups.TITLE_RES, 1143 Groups.GROUP_VISIBLE, 1144 Groups.SYSTEM_ID, 1145 Groups.DELETED, 1146 Groups.NOTES, 1147 Groups.SHOULD_SYNC, 1148 Groups.FAVORITES, 1149 Groups.AUTO_ADD, 1150 Groups.GROUP_IS_READ_ONLY, 1151 Groups.SYNC1, 1152 Groups.SYNC2, 1153 Groups.SYNC3, 1154 Groups.SYNC4, 1155 }); 1156 } 1157 1158 @Test testGroupsSummaryProjection()1159 public void testGroupsSummaryProjection() { 1160 assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{ 1161 Groups._ID, 1162 Groups.ACCOUNT_NAME, 1163 Groups.ACCOUNT_TYPE, 1164 Groups.DATA_SET, 1165 Groups.ACCOUNT_TYPE_AND_DATA_SET, 1166 Groups.SOURCE_ID, 1167 Groups.DIRTY, 1168 Groups.VERSION, 1169 Groups.RES_PACKAGE, 1170 Groups.TITLE, 1171 Groups.TITLE_RES, 1172 Groups.GROUP_VISIBLE, 1173 Groups.SYSTEM_ID, 1174 Groups.DELETED, 1175 Groups.NOTES, 1176 Groups.SHOULD_SYNC, 1177 Groups.FAVORITES, 1178 Groups.AUTO_ADD, 1179 Groups.GROUP_IS_READ_ONLY, 1180 Groups.SYNC1, 1181 Groups.SYNC2, 1182 Groups.SYNC3, 1183 Groups.SYNC4, 1184 Groups.SUMMARY_COUNT, 1185 Groups.SUMMARY_WITH_PHONES, 1186 Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1187 }); 1188 } 1189 1190 @Test testAggregateExceptionProjection()1191 public void testAggregateExceptionProjection() { 1192 assertProjection(AggregationExceptions.CONTENT_URI, new String[]{ 1193 AggregationExceptionColumns._ID, 1194 AggregationExceptions.TYPE, 1195 AggregationExceptions.RAW_CONTACT_ID1, 1196 AggregationExceptions.RAW_CONTACT_ID2, 1197 }); 1198 } 1199 1200 @Test testSettingsProjection()1201 public void testSettingsProjection() { 1202 assertProjection(Settings.CONTENT_URI, new String[]{ 1203 Settings.ACCOUNT_NAME, 1204 Settings.ACCOUNT_TYPE, 1205 Settings.DATA_SET, 1206 Settings.UNGROUPED_VISIBLE, 1207 Settings.SHOULD_SYNC, 1208 Settings.ANY_UNSYNCED, 1209 Settings.UNGROUPED_COUNT, 1210 Settings.UNGROUPED_WITH_PHONES, 1211 }); 1212 } 1213 1214 @Test testStatusUpdatesProjection()1215 public void testStatusUpdatesProjection() { 1216 assertProjection(StatusUpdates.CONTENT_URI, new String[]{ 1217 PresenceColumns.RAW_CONTACT_ID, 1218 StatusUpdates.DATA_ID, 1219 StatusUpdates.IM_ACCOUNT, 1220 StatusUpdates.IM_HANDLE, 1221 StatusUpdates.PROTOCOL, 1222 StatusUpdates.CUSTOM_PROTOCOL, 1223 StatusUpdates.PRESENCE, 1224 StatusUpdates.CHAT_CAPABILITY, 1225 StatusUpdates.STATUS, 1226 StatusUpdates.STATUS_TIMESTAMP, 1227 StatusUpdates.STATUS_RES_PACKAGE, 1228 StatusUpdates.STATUS_ICON, 1229 StatusUpdates.STATUS_LABEL, 1230 }); 1231 } 1232 1233 @Test testDirectoryProjection()1234 public void testDirectoryProjection() { 1235 assertProjection(Directory.CONTENT_URI, new String[]{ 1236 Directory._ID, 1237 Directory.PACKAGE_NAME, 1238 Directory.TYPE_RESOURCE_ID, 1239 Directory.DISPLAY_NAME, 1240 Directory.DIRECTORY_AUTHORITY, 1241 Directory.ACCOUNT_TYPE, 1242 Directory.ACCOUNT_NAME, 1243 Directory.EXPORT_SUPPORT, 1244 Directory.SHORTCUT_SUPPORT, 1245 Directory.PHOTO_SUPPORT, 1246 }); 1247 } 1248 1249 @Test testProviderStatusProjection()1250 public void testProviderStatusProjection() { 1251 assertProjection(ProviderStatus.CONTENT_URI, new String[]{ 1252 ProviderStatus.STATUS, 1253 ProviderStatus.DATABASE_CREATION_TIMESTAMP, 1254 }); 1255 } 1256 1257 @Test testRawContactsInsert()1258 public void testRawContactsInsert() { 1259 ContentValues values = new ContentValues(); 1260 1261 values.put(RawContacts.ACCOUNT_NAME, "a"); 1262 values.put(RawContacts.ACCOUNT_TYPE, "b"); 1263 values.put(RawContacts.DATA_SET, "ds"); 1264 values.put(RawContacts.SOURCE_ID, "c"); 1265 values.put(RawContacts.VERSION, 42); 1266 values.put(RawContacts.DIRTY, 1); 1267 values.put(RawContacts.DELETED, 1); 1268 values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); 1269 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1270 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1271 values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 123); 1272 values.put(RawContacts.TIMES_CONTACTED, 12); 1273 values.put(RawContacts.STARRED, 1); 1274 values.put(RawContacts.SYNC1, "e"); 1275 values.put(RawContacts.SYNC2, "f"); 1276 values.put(RawContacts.SYNC3, "g"); 1277 values.put(RawContacts.SYNC4, "h"); 1278 1279 Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1280 long rawContactId = ContentUris.parseId(rowUri); 1281 1282 values.put(RawContacts.LAST_TIME_CONTACTED, 0); 1283 values.put(RawContacts.TIMES_CONTACTED, 0); 1284 1285 assertStoredValues(rowUri, values); 1286 assertNetworkNotified(true); 1287 } 1288 1289 @Test testDataDirectoryWithLookupUri()1290 public void testDataDirectoryWithLookupUri() { 1291 ContentValues values = new ContentValues(); 1292 1293 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 1294 insertPhoneNumber(rawContactId, "555-GOOG-411"); 1295 insertEmail(rawContactId, "[email protected]"); 1296 1297 long contactId = queryContactId(rawContactId); 1298 String lookupKey = queryLookupKey(contactId); 1299 1300 // Complete and valid lookup URI 1301 Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 1302 Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 1303 1304 assertDataRows(dataUri, values); 1305 1306 // Complete but stale lookup URI 1307 lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 1308 dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 1309 assertDataRows(dataUri, values); 1310 1311 // Incomplete lookup URI (lookup key only, no contact ID) 1312 dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, 1313 lookupKey), Contacts.Data.CONTENT_DIRECTORY); 1314 assertDataRows(dataUri, values); 1315 } 1316 assertDataRows(Uri dataUri, ContentValues values)1317 private void assertDataRows(Uri dataUri, ContentValues values) { 1318 Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID); 1319 assertEquals(3, cursor.getCount()); 1320 cursor.moveToFirst(); 1321 values.put(Data.DATA1, "John Doe"); 1322 assertCursorValues(cursor, values); 1323 1324 cursor.moveToNext(); 1325 values.put(Data.DATA1, "555-GOOG-411"); 1326 assertCursorValues(cursor, values); 1327 1328 cursor.moveToNext(); 1329 values.put(Data.DATA1, "[email protected]"); 1330 assertCursorValues(cursor, values); 1331 1332 cursor.close(); 1333 } 1334 1335 @Test testContactEntitiesWithIdBasedUri()1336 public void testContactEntitiesWithIdBasedUri() { 1337 ContentValues values = new ContentValues(); 1338 Account account1 = new Account("act1", "actype1"); 1339 Account account2 = new Account("act2", "actype2"); 1340 1341 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1); 1342 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 1343 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 1344 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 1345 1346 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 1347 setAggregationException( 1348 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 1349 1350 long contactId = queryContactId(rawContactId1); 1351 1352 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1353 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY); 1354 1355 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 1356 } 1357 1358 @Test testContactEntitiesWithLookupUri()1359 public void testContactEntitiesWithLookupUri() { 1360 ContentValues values = new ContentValues(); 1361 Account account1 = new Account("act1", "actype1"); 1362 Account account2 = new Account("act2", "actype2"); 1363 1364 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1); 1365 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 1366 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 1367 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 1368 1369 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 1370 setAggregationException( 1371 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 1372 1373 long contactId = queryContactId(rawContactId1); 1374 String lookupKey = queryLookupKey(contactId); 1375 1376 // First try with a matching contact ID 1377 Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 1378 Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 1379 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 1380 1381 // Now try with a contact ID mismatch 1382 contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 1383 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 1384 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 1385 1386 // Now try without an ID altogether 1387 contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey); 1388 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 1389 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 1390 } 1391 assertEntityRows(Uri entityUri, long contactId, long rawContactId1, long rawContactId2)1392 private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1, 1393 long rawContactId2) { 1394 ContentValues values = new ContentValues(); 1395 1396 Cursor cursor = mResolver.query(entityUri, null, null, null, 1397 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID); 1398 assertEquals(3, cursor.getCount()); 1399 1400 // First row - name 1401 cursor.moveToFirst(); 1402 values.put(Contacts.Entity.CONTACT_ID, contactId); 1403 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 1404 values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 1405 values.put(Contacts.Entity.DATA1, "John Doe"); 1406 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 1407 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 1408 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 1409 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 1410 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 1411 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1412 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 1413 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 1414 values.putNull(Contacts.Entity.PRESENCE); 1415 assertCursorValues(cursor, values); 1416 1417 // Second row - IM 1418 cursor.moveToNext(); 1419 values.put(Contacts.Entity.CONTACT_ID, contactId); 1420 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 1421 values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE); 1422 values.put(Contacts.Entity.DATA1, "gtalk"); 1423 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 1424 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 1425 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 1426 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 1427 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 1428 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1429 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 1430 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 1431 values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE); 1432 assertCursorValues(cursor, values); 1433 1434 // Third row - second raw contact, not data 1435 cursor.moveToNext(); 1436 values.put(Contacts.Entity.CONTACT_ID, contactId); 1437 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2); 1438 values.putNull(Contacts.Entity.MIMETYPE); 1439 values.putNull(Contacts.Entity.DATA_ID); 1440 values.putNull(Contacts.Entity.DATA1); 1441 values.put(Contacts.Entity.ACCOUNT_NAME, "act2"); 1442 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2"); 1443 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 1444 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 1445 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 1446 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1447 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 1448 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 1449 values.putNull(Contacts.Entity.PRESENCE); 1450 assertCursorValues(cursor, values); 1451 1452 cursor.close(); 1453 } 1454 1455 @Test testDataInsert()1456 public void testDataInsert() { 1457 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1458 1459 ContentValues values = new ContentValues(); 1460 putDataValues(values, rawContactId); 1461 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1462 long dataId = ContentUris.parseId(dataUri); 1463 1464 long contactId = queryContactId(rawContactId); 1465 values.put(RawContacts.CONTACT_ID, contactId); 1466 assertStoredValues(dataUri, values); 1467 1468 values.remove(Photo.PHOTO);// Remove byte[] value. 1469 assertSelection(Data.CONTENT_URI, values, Data._ID, dataId); 1470 1471 // Access the same data through the directory under RawContacts 1472 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 1473 Uri rawContactDataUri = 1474 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY); 1475 assertSelection(rawContactDataUri, values, Data._ID, dataId); 1476 1477 // Access the same data through the directory under Contacts 1478 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1479 Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY); 1480 assertSelection(contactDataUri, values, Data._ID, dataId); 1481 assertNetworkNotified(true); 1482 } 1483 1484 @Test testDataInsertAndUpdateHashId()1485 public void testDataInsertAndUpdateHashId() { 1486 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1487 1488 // Insert a data with non-photo mimetype. 1489 ContentValues values = new ContentValues(); 1490 putDataValues(values, rawContactId); 1491 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1492 1493 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 1494 final ContactsDatabaseHelper helper = cp.getDatabaseHelper(); 1495 String data1 = values.getAsString(Data.DATA1); 1496 String data2 = values.getAsString(Data.DATA2); 1497 String combineString = data1+data2; 1498 String hashId = helper.generateHashIdForData(combineString.getBytes()); 1499 assertStoredValue(dataUri, Data.HASH_ID, hashId); 1500 1501 // Update the data with primary, and check if hash_id is not changed. 1502 values.remove(Data.DATA1); 1503 values.remove(Data.DATA2); 1504 values.remove(Data.DATA15); 1505 values.put(Data.IS_PRIMARY, "1"); 1506 mResolver.update(dataUri, values, null, null); 1507 assertStoredValue(dataUri, Data.IS_PRIMARY, "1"); 1508 assertStoredValue(dataUri, Data.HASH_ID, hashId); 1509 1510 // Update the data with new data1. 1511 values = new ContentValues(); 1512 putDataValues(values, rawContactId); 1513 String newData1 = "Newone"; 1514 values.put(Data.DATA1, newData1); 1515 mResolver.update(dataUri, values, null, null); 1516 combineString = newData1+data2; 1517 String newHashId = helper.generateHashIdForData(combineString.getBytes()); 1518 assertStoredValue(dataUri, Data.HASH_ID, newHashId); 1519 1520 // Update the data with a new Data2. 1521 values.remove(Data.DATA1); 1522 values.put(Data.DATA2, "Newtwo"); 1523 combineString = "NewoneNewtwo"; 1524 String testHashId = helper.generateHashIdForData(combineString.getBytes()); 1525 mResolver.update(dataUri, values, null, null); 1526 assertStoredValue(dataUri, Data.HASH_ID, testHashId); 1527 1528 // Update the data with a new data1 + data2. 1529 values.put(Data.DATA1, "one"); 1530 combineString = "oneNewtwo"; 1531 testHashId = helper.generateHashIdForData(combineString.getBytes()); 1532 mResolver.update(dataUri, values, null, null); 1533 assertStoredValue(dataUri, Data.HASH_ID, testHashId); 1534 1535 // Update the data with null data1 and null data2. 1536 values.putNull(Data.DATA1); 1537 values.putNull(Data.DATA2); 1538 mResolver.update(dataUri, values, null, null); 1539 assertStoredValue(dataUri, Data.HASH_ID, null); 1540 } 1541 1542 @Test testDataInsertAndUpdateHashId_Photo()1543 public void testDataInsertAndUpdateHashId_Photo() { 1544 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1545 1546 // Insert a data with photo mimetype. 1547 ContentValues values = new ContentValues(); 1548 values.put(Data.RAW_CONTACT_ID, rawContactId); 1549 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 1550 values.put(Data.DATA1, "testData1"); 1551 values.put(Data.DATA2, "testData2"); 1552 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1553 1554 // Check for photo data's hashId is correct or not. 1555 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 1556 final ContactsDatabaseHelper helper = cp.getDatabaseHelper(); 1557 String hashId = helper.getPhotoHashId(); 1558 assertStoredValue(dataUri, Data.HASH_ID, hashId); 1559 1560 // Update the data with new DATA1, and check if hash_id is not changed. 1561 values.put(Data.DATA1, "newData1"); 1562 mResolver.update(dataUri, values, null, null); 1563 assertStoredValue(dataUri, Data.DATA1, "newData1"); 1564 assertStoredValue(dataUri, Data.HASH_ID, hashId); 1565 } 1566 1567 @Test testDataInsertPhoneNumberTooLongIsTrimmed()1568 public void testDataInsertPhoneNumberTooLongIsTrimmed() { 1569 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1570 1571 ContentValues values = new ContentValues(); 1572 values.put(Data.RAW_CONTACT_ID, rawContactId); 1573 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1574 final StringBuilder sb = new StringBuilder(); 1575 for (int i = 0; i < 300; i++) { 1576 sb.append("12345"); 1577 } 1578 final String phoneNumber1500Chars = sb.toString(); 1579 values.put(Phone.NUMBER, phoneNumber1500Chars); 1580 1581 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1582 final long dataId = ContentUris.parseId(dataUri); 1583 1584 sb.setLength(0); 1585 for (int i = 0; i < 200; i++) { 1586 sb.append("12345"); 1587 } 1588 final String phoneNumber1000Chars = sb.toString(); 1589 final ContentValues expected = new ContentValues(); 1590 expected.put(Phone.NUMBER, phoneNumber1000Chars); 1591 assertSelection(dataUri, expected, Data._ID, dataId); 1592 } 1593 1594 @Test testRawContactDataQuery()1595 public void testRawContactDataQuery() { 1596 Account account1 = new Account("a", "b"); 1597 Account account2 = new Account("c", "d"); 1598 long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1); 1599 Uri dataUri1 = DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); 1600 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 1601 Uri dataUri2 = DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doe"); 1602 1603 Uri uri1 = TestUtil.maybeAddAccountQueryParameters(dataUri1, account1); 1604 Uri uri2 = TestUtil.maybeAddAccountQueryParameters(dataUri2, account2); 1605 assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ; 1606 assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ; 1607 } 1608 1609 @Test testPhonesQuery()1610 public void testPhonesQuery() { 1611 1612 ContentValues values = new ContentValues(); 1613 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1614 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1615 values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 5); 1616 values.put(RawContacts.TIMES_CONTACTED, 54321); 1617 values.put(RawContacts.STARRED, 1); 1618 1619 Uri rawContactUri = insertRawContact(values); 1620 long rawContactId = ContentUris.parseId(rawContactUri); 1621 1622 DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox"); 1623 Uri uri = insertPhoneNumber(rawContactId, "18004664411"); 1624 long phoneId = ContentUris.parseId(uri); 1625 1626 1627 long contactId = queryContactId(rawContactId); 1628 values.clear(); 1629 values.put(Data._ID, phoneId); 1630 values.put(Data.RAW_CONTACT_ID, rawContactId); 1631 values.put(RawContacts.CONTACT_ID, contactId); 1632 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1633 values.put(Phone.NUMBER, "18004664411"); 1634 values.put(Phone.TYPE, Phone.TYPE_HOME); 1635 values.putNull(Phone.LABEL); 1636 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 1637 values.put(Contacts.CUSTOM_RINGTONE, "d"); 1638 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 1639 values.put(Contacts.LAST_TIME_CONTACTED, 0); 1640 values.put(Contacts.TIMES_CONTACTED, 0); 1641 values.put(Contacts.STARRED, 1); 1642 1643 assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values); 1644 } 1645 1646 @Test testPhonesWithMergedContacts()1647 public void testPhonesWithMergedContacts() { 1648 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 1649 insertPhoneNumber(rawContactId1, "123456789", true); 1650 1651 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 1652 insertPhoneNumber(rawContactId2, "123456789", true); 1653 1654 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 1655 rawContactId1, rawContactId2); 1656 assertNotAggregated(rawContactId1, rawContactId2); 1657 1658 ContentValues values1 = new ContentValues(); 1659 values1.put(Contacts.DISPLAY_NAME, "123456789"); 1660 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1661 values1.put(Phone.NUMBER, "123456789"); 1662 1663 // There are two phone numbers, so we should get two rows. 1664 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 1665 1666 // Now set the dedupe flag. But still we should get two rows, because they're two 1667 // different contacts. We only dedupe within each contact. 1668 final Uri dedupeUri = Phone.CONTENT_URI.buildUpon() 1669 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 1670 .build(); 1671 assertStoredValues(dedupeUri, new ContentValues[] {values1, values1}); 1672 1673 // Now join them into a single contact. 1674 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1675 rawContactId1, rawContactId2); 1676 1677 assertAggregated(rawContactId1, rawContactId2, "123456789"); 1678 1679 // Contact merge won't affect the default result of Phone Uri, where we don't dedupe. 1680 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 1681 1682 // Now we dedupe them. 1683 assertStoredValues(dedupeUri, values1); 1684 } 1685 1686 @Test testPhonesNormalizedNumber()1687 public void testPhonesNormalizedNumber() { 1688 final long rawContactId = RawContactUtil.createRawContact(mResolver); 1689 1690 // Write both a number and a normalized number. Those should be written as-is 1691 final ContentValues values = new ContentValues(); 1692 values.put(Data.RAW_CONTACT_ID, rawContactId); 1693 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1694 values.put(Phone.NUMBER, "1234"); 1695 values.put(Phone.NORMALIZED_NUMBER, "5678"); 1696 values.put(Phone.TYPE, Phone.TYPE_HOME); 1697 1698 final Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1699 1700 // Check the lookup table. 1701 assertEquals(1, 1702 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null)); 1703 assertEquals(1, 1704 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null)); 1705 1706 // Check the data table. 1707 assertStoredValues(dataUri, 1708 cv(Phone.NUMBER, "1234", Phone.NORMALIZED_NUMBER, "5678") 1709 ); 1710 1711 // Replace both in an UPDATE 1712 values.clear(); 1713 values.put(Phone.NUMBER, "4321"); 1714 values.put(Phone.NORMALIZED_NUMBER, "8765"); 1715 mResolver.update(dataUri, values, null, null); 1716 assertEquals(0, 1717 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null)); 1718 assertEquals(1, 1719 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "4321"), null, null)); 1720 assertEquals(0, 1721 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null)); 1722 assertEquals(1, 1723 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1724 1725 assertStoredValues(dataUri, 1726 cv(Phone.NUMBER, "4321", Phone.NORMALIZED_NUMBER, "8765") 1727 ); 1728 1729 // Replace only NUMBER ==> NORMALIZED_NUMBER will be inferred (we test that by making 1730 // sure the old manual value can not be found anymore) 1731 values.clear(); 1732 values.put(Phone.NUMBER, "+1-800-466-5432"); 1733 mResolver.update(dataUri, values, null, null); 1734 assertEquals( 1735 1, 1736 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null, 1737 null)); 1738 assertEquals(0, 1739 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1740 1741 assertStoredValues(dataUri, 1742 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432") 1743 ); 1744 1745 // Replace only NORMALIZED_NUMBER ==> call is ignored, things will be unchanged 1746 values.clear(); 1747 values.put(Phone.NORMALIZED_NUMBER, "8765"); 1748 mResolver.update(dataUri, values, null, null); 1749 assertEquals( 1750 1, 1751 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null, 1752 null)); 1753 assertEquals(0, 1754 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1755 1756 assertStoredValues(dataUri, 1757 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432") 1758 ); 1759 1760 // Replace NUMBER with an "invalid" number which can't be normalized. It should clear 1761 // NORMALIZED_NUMBER. 1762 1763 // 1. Set 999 to NORMALIZED_NUMBER explicitly. 1764 values.clear(); 1765 values.put(Phone.NUMBER, "888"); 1766 values.put(Phone.NORMALIZED_NUMBER, "999"); 1767 mResolver.update(dataUri, values, null, null); 1768 1769 assertEquals(1, 1770 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null)); 1771 1772 assertStoredValues(dataUri, 1773 cv(Phone.NUMBER, "888", Phone.NORMALIZED_NUMBER, "999") 1774 ); 1775 1776 // 2. Set an invalid number to NUMBER. 1777 values.clear(); 1778 values.put(Phone.NUMBER, "1"); 1779 mResolver.update(dataUri, values, null, null); 1780 1781 assertEquals(0, 1782 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null)); 1783 1784 assertStoredValues(dataUri, 1785 cv(Phone.NUMBER, "1", Phone.NORMALIZED_NUMBER, null) 1786 ); 1787 } 1788 1789 @Test testPhonesFilterQuery()1790 public void testPhonesFilterQuery() { 1791 testPhonesFilterQueryInter(Phone.CONTENT_FILTER_URI); 1792 } 1793 1794 /** 1795 * A convenient method for {@link #testPhonesFilterQuery()} and 1796 * {@link #testCallablesFilterQuery()}. 1797 * 1798 * This confirms if both URIs return identical results for phone-only contacts and 1799 * appropriately different results for contacts with sip addresses. 1800 * 1801 * @param baseFilterUri Either {@link Phone#CONTENT_FILTER_URI} or 1802 * {@link Callable#CONTENT_FILTER_URI}. 1803 */ testPhonesFilterQueryInter(Uri baseFilterUri)1804 private void testPhonesFilterQueryInter(Uri baseFilterUri) { 1805 assertTrue("Unsupported Uri (" + baseFilterUri + ")", 1806 Phone.CONTENT_FILTER_URI.equals(baseFilterUri) 1807 || Callable.CONTENT_FILTER_URI.equals(baseFilterUri)); 1808 1809 final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", 1810 "Tamale", TestUtil.ACCOUNT_1); 1811 insertPhoneNumber(rawContactId1, "1-800-466-4411"); 1812 1813 final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Chilled", 1814 "Guacamole", TestUtil.ACCOUNT_2); 1815 insertPhoneNumber(rawContactId2, "1-800-466-5432"); 1816 insertPhoneNumber(rawContactId2, "[email protected]", false, Phone.TYPE_PAGER); 1817 insertPhoneNumber(rawContactId2, "[email protected]", false, Phone.TYPE_PAGER); 1818 1819 final Uri filterUri1 = Uri.withAppendedPath(baseFilterUri, "tamale"); 1820 ContentValues values = new ContentValues(); 1821 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1822 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1823 values.put(Phone.NUMBER, "1-800-466-4411"); 1824 values.put(Phone.TYPE, Phone.TYPE_HOME); 1825 values.putNull(Phone.LABEL); 1826 assertStoredValuesWithProjection(filterUri1, values); 1827 1828 final Uri filterUri2 = Uri.withAppendedPath(baseFilterUri, "1-800-GOOG-411"); 1829 assertStoredValues(filterUri2, values); 1830 1831 final Uri filterUri3 = Uri.withAppendedPath(baseFilterUri, "18004664"); 1832 assertStoredValues(filterUri3, values); 1833 1834 final Uri filterUri4 = Uri.withAppendedPath(baseFilterUri, "encilada"); 1835 assertEquals(0, getCount(filterUri4, null, null)); 1836 1837 final Uri filterUri5 = Uri.withAppendedPath(baseFilterUri, "*"); 1838 assertEquals(0, getCount(filterUri5, null, null)); 1839 1840 ContentValues values1 = new ContentValues(); 1841 values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1842 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1843 values1.put(Phone.NUMBER, "1-800-466-5432"); 1844 values1.put(Phone.TYPE, Phone.TYPE_HOME); 1845 values1.putNull(Phone.LABEL); 1846 1847 ContentValues values2 = new ContentValues(); 1848 values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1849 values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1850 values2.put(Phone.NUMBER, "[email protected]"); 1851 values2.put(Phone.TYPE, Phone.TYPE_PAGER); 1852 values2.putNull(Phone.LABEL); 1853 1854 ContentValues values3 = new ContentValues(); 1855 values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1856 values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1857 values3.put(Phone.NUMBER, "[email protected]"); 1858 values3.put(Phone.TYPE, Phone.TYPE_PAGER); 1859 values3.putNull(Phone.LABEL); 1860 1861 final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled"); 1862 assertStoredValues(filterUri6, new ContentValues[]{values1, values2, values3}); 1863 1864 // Insert a SIP address. From here, Phone URI and Callable URI may return different results 1865 // than each other. 1866 insertSipAddress(rawContactId1, "[email protected]"); 1867 insertSipAddress(rawContactId1, "sip:[email protected]"); 1868 1869 final Uri filterUri7 = Uri.withAppendedPath(baseFilterUri, "sip_hot"); 1870 final Uri filterUri8 = Uri.withAppendedPath(baseFilterUri, "sip_hot_tamale"); 1871 if (Callable.CONTENT_FILTER_URI.equals(baseFilterUri)) { 1872 ContentValues values4 = new ContentValues(); 1873 values4.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1874 values4.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 1875 values4.put(SipAddress.SIP_ADDRESS, "[email protected]"); 1876 1877 ContentValues values5 = new ContentValues(); 1878 values5.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1879 values5.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 1880 values5.put(SipAddress.SIP_ADDRESS, "sip:[email protected]"); 1881 assertStoredValues(filterUri1, new ContentValues[] {values, values4, values5}); 1882 1883 assertStoredValues(filterUri7, new ContentValues[] {values4, values5}); 1884 assertStoredValues(filterUri8, values4); 1885 } else { 1886 // Sip address should not affect Phone URI. 1887 assertStoredValuesWithProjection(filterUri1, values); 1888 assertEquals(0, getCount(filterUri7, null, null)); 1889 } 1890 1891 // Sanity test. Run tests for "Chilled Guacamole" again and see nothing changes 1892 // after the Sip address being inserted. 1893 assertStoredValues(filterUri2, values); 1894 assertEquals(0, getCount(filterUri4, null, null)); 1895 assertEquals(0, getCount(filterUri5, null, null)); 1896 assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} ); 1897 } 1898 1899 @Test testPhonesFilterSearchParams()1900 public void testPhonesFilterSearchParams() { 1901 final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "Dad", null); 1902 insertPhoneNumber(rid1, "123-456-7890"); 1903 1904 final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "Mam", null); 1905 insertPhoneNumber(rid2, "323-123-4567"); 1906 1907 // By default, "dad" will match both the display name and the phone number. 1908 // Because "dad" is "323" after the dialpad conversion, it'll match "Mam" too. 1909 assertStoredValues( 1910 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad").build(), 1911 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890"), 1912 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567") 1913 ); 1914 assertStoredValues( 1915 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1916 .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") 1917 .build(), 1918 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890") 1919 ); 1920 1921 assertStoredValues( 1922 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1923 .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") 1924 .build(), 1925 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567") 1926 ); 1927 assertStoredValues( 1928 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1929 .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") 1930 .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") 1931 .build() 1932 ); 1933 } 1934 1935 @Test testPhoneLookup()1936 public void testPhoneLookup() { 1937 ContentValues values = new ContentValues(); 1938 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1939 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1940 1941 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1942 long rawContactId = ContentUris.parseId(rawContactUri); 1943 1944 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 1945 long dataId = 1946 Long.parseLong(insertPhoneNumber(rawContactId, "18004664411").getLastPathSegment()); 1947 1948 // We'll create two lookup records, 18004664411 and +18004664411, and the below lookup 1949 // will match both. 1950 1951 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 1952 1953 values.clear(); 1954 values.put(PhoneLookup._ID, queryContactId(rawContactId)); 1955 values.put(PhoneLookup.CONTACT_ID, queryContactId(rawContactId)); 1956 values.put(PhoneLookup.DATA_ID, dataId); 1957 values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1958 values.put(PhoneLookup.NUMBER, "18004664411"); 1959 values.put(PhoneLookup.TYPE, Phone.TYPE_HOME); 1960 values.putNull(PhoneLookup.LABEL); 1961 values.put(PhoneLookup.CUSTOM_RINGTONE, "d"); 1962 values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1); 1963 assertStoredValues(lookupUri1, null, null, new ContentValues[] {values, values}); 1964 1965 // In the context that 8004664411 is a valid number, "4664411" as a 1966 // call id should not match to either "8004664411" or "+18004664411". 1967 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411"); 1968 assertEquals(0, getCount(lookupUri2, null, null)); 1969 1970 // A wrong area code 799 vs 800 should not be matched 1971 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "7994664411"); 1972 assertEquals(0, getCount(lookupUri2, null, null)); 1973 } 1974 1975 @Test testSipPhoneLookup()1976 public void testSipPhoneLookup() { 1977 ContentValues values = new ContentValues(); 1978 1979 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1980 long rawContactId = ContentUris.parseId(rawContactUri); 1981 1982 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 1983 long dataId = 1984 Long.parseLong(insertSipAddress(rawContactId, "abc@sip").getLastPathSegment()); 1985 1986 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "abc@sip") 1987 .buildUpon() 1988 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1") 1989 .build(); 1990 1991 values.clear(); 1992 values.put(PhoneLookup._ID, dataId); 1993 values.put(PhoneLookup.CONTACT_ID, queryContactId(rawContactId)); 1994 values.put(PhoneLookup.DATA_ID, dataId); 1995 values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1996 values.put(PhoneLookup.NUMBER, "abc@sip"); 1997 values.putNull(PhoneLookup.LABEL); 1998 assertStoredValues(lookupUri1, null, null, new ContentValues[] {values}); 1999 2000 // A wrong sip address should not be matched 2001 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "wrong@sip"); 2002 assertEquals(0, getCount(lookupUri2, null, null)); 2003 } 2004 2005 @Test testPhoneLookupStarUseCases()2006 public void testPhoneLookupStarUseCases() { 2007 // Create two raw contacts with numbers "*123" and "12 3". This is a real life example 2008 // from b/13195334. 2009 final ContentValues values = new ContentValues(); 2010 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2011 long rawContactId = ContentUris.parseId(rawContactUri); 2012 DataUtil.insertStructuredName(mResolver, rawContactId, "Emergency", /* familyName =*/ null); 2013 insertPhoneNumber(rawContactId, "*123"); 2014 2015 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2016 rawContactId = ContentUris.parseId(rawContactUri); 2017 DataUtil.insertStructuredName(mResolver, rawContactId, "Voicemail", /* familyName =*/ null); 2018 insertPhoneNumber(rawContactId, "12 3"); 2019 2020 // Verify: "123" returns the "Voicemail" raw contact id. It should not match 2021 // a phone number that starts with a "*". 2022 Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123"); 2023 values.clear(); 2024 values.put(PhoneLookup.DISPLAY_NAME, "Voicemail"); 2025 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 2026 2027 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "(1) 23"); 2028 values.clear(); 2029 values.put(PhoneLookup.DISPLAY_NAME, "Voicemail"); 2030 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 2031 2032 // Verify: "*123" returns the "Emergency" raw contact id. 2033 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*1-23"); 2034 values.clear(); 2035 values.put(PhoneLookup.DISPLAY_NAME, "Emergency"); 2036 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 2037 } 2038 2039 @Test testPhoneLookupReturnsNothingRatherThanStar()2040 public void testPhoneLookupReturnsNothingRatherThanStar() { 2041 // Create Emergency raw contact with "*123456789" number. 2042 final ContentValues values = new ContentValues(); 2043 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2044 final long rawContactId1 = ContentUris.parseId(rawContactUri); 2045 DataUtil.insertStructuredName(mResolver, rawContactId1, "Emergency", 2046 /* familyName =*/ null); 2047 insertPhoneNumber(rawContactId1, "*123456789"); 2048 2049 // Lookup should return no results. It does not ignore stars even when no other matches. 2050 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123456789"); 2051 assertEquals(0, getCount(lookupUri, null, null)); 2052 } 2053 2054 @Test testPhoneLookupReturnsNothingRatherThanMissStar()2055 public void testPhoneLookupReturnsNothingRatherThanMissStar() { 2056 // Create Voice Mail raw contact with "123456789" number. 2057 final ContentValues values = new ContentValues(); 2058 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2059 final long rawContactId1 = ContentUris.parseId(rawContactUri); 2060 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 2061 /* familyName =*/ null); 2062 insertPhoneNumber(rawContactId1, "123456789"); 2063 2064 // Lookup should return no results. It does not ignore stars even when no other matches. 2065 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*123456789"); 2066 assertEquals(0, getCount(lookupUri, null, null)); 2067 } 2068 2069 @Test testPhoneLookupStarNoFallbackMatch()2070 public void testPhoneLookupStarNoFallbackMatch() { 2071 final ContentValues values = new ContentValues(); 2072 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2073 final long rawContactId1 = ContentUris.parseId(rawContactUri); 2074 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 2075 /* familyName =*/ null); 2076 insertPhoneNumber(rawContactId1, "*011123456789"); 2077 2078 // The numbers "+123456789" and "*011123456789" are a "fallback" match. The + is equivalent 2079 // to "011". This lookup should return no results. Lookup does not ignore 2080 // stars, even when doing a fallback lookup. 2081 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+123456789"); 2082 assertEquals(0, getCount(lookupUri, null, null)); 2083 } 2084 2085 @Test testPhoneLookupStarNotBreakFallbackMatching()2086 public void testPhoneLookupStarNotBreakFallbackMatching() { 2087 // Create a raw contact with a phone number starting with "011" 2088 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 2089 long rawContactId = ContentUris.parseId(rawContactUri); 2090 DataUtil.insertStructuredName(mResolver, rawContactId, "No star", 2091 /* familyName =*/ null); 2092 insertPhoneNumber(rawContactId, "011123456789"); 2093 2094 // Create a raw contact with a phone number starting with "*011" 2095 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 2096 rawContactId = ContentUris.parseId(rawContactUri); 2097 DataUtil.insertStructuredName(mResolver, rawContactId, "Has star", 2098 /* familyName =*/ null); 2099 insertPhoneNumber(rawContactId, "*011123456789"); 2100 2101 // A phone number starting with "+" can (fallback) match the same phone number starting 2102 // with "001". Verify that this fallback matching still occurs in the presence of 2103 // numbers starting with "*"s. 2104 final Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, 2105 "+123456789"); 2106 final ContentValues values = new ContentValues(); 2107 values.put(PhoneLookup.DISPLAY_NAME, "No star"); 2108 assertStoredValues(lookupUri1, null, null, new ContentValues[]{values}); 2109 } 2110 2111 @Test testPhoneLookupExplicitProjection()2112 public void testPhoneLookupExplicitProjection() { 2113 final ContentValues values = new ContentValues(); 2114 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2115 final long rawContactId1 = ContentUris.parseId(rawContactUri); 2116 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 2117 /* familyName =*/ null); 2118 insertPhoneNumber(rawContactId1, "+1234567"); 2119 2120 // Performing a query with a non-null projection with or without PhoneLookup.Number inside 2121 // it should not cause a crash. 2122 Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "1234567"); 2123 String[] projection = new String[] {PhoneLookup.DISPLAY_NAME}; 2124 mResolver.query(lookupUri, projection, null, null, null); 2125 projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER}; 2126 mResolver.query(lookupUri, projection, null, null, null); 2127 2128 // Shouldn't crash for a fallback query either 2129 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*0111234567"); 2130 projection = new String[] {PhoneLookup.DISPLAY_NAME}; 2131 mResolver.query(lookupUri, projection, null, null, null); 2132 projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER}; 2133 mResolver.query(lookupUri, projection, null, null, null); 2134 } 2135 2136 @Test testPhoneLookupUseCases()2137 public void testPhoneLookupUseCases() { 2138 ContentValues values = new ContentValues(); 2139 Uri rawContactUri; 2140 long rawContactId; 2141 Uri lookupUri2; 2142 2143 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2144 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2145 2146 // International format in contacts 2147 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2148 rawContactId = ContentUris.parseId(rawContactUri); 2149 2150 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 2151 insertPhoneNumber(rawContactId, "+1-650-861-0000"); 2152 2153 values.clear(); 2154 2155 // match with international format 2156 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000"); 2157 assertEquals(1, getCount(lookupUri2, null, null)); 2158 2159 // match with national format 2160 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000"); 2161 assertEquals(1, getCount(lookupUri2, null, null)); 2162 2163 // does not match with wrong area code 2164 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "649 861 0000"); 2165 assertEquals(0, getCount(lookupUri2, null, null)); 2166 2167 // does not match with missing digits in mistyped area code 2168 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "5 861 0000"); 2169 assertEquals(0, getCount(lookupUri2, null, null)); 2170 2171 // does not match with missing digit in mistyped area code 2172 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "65 861 0000"); 2173 assertEquals(0, getCount(lookupUri2, null, null)); 2174 2175 // National format in contacts 2176 values.clear(); 2177 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2178 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2179 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2180 rawContactId = ContentUris.parseId(rawContactUri); 2181 2182 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot1", "Tamale"); 2183 insertPhoneNumber(rawContactId, "650-861-0001"); 2184 2185 values.clear(); 2186 2187 // match with international format 2188 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001"); 2189 assertEquals(2, getCount(lookupUri2, null, null)); 2190 2191 // match with national format 2192 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001"); 2193 assertEquals(2, getCount(lookupUri2, null, null)); 2194 2195 // Local format in contacts 2196 values.clear(); 2197 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2198 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2199 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2200 rawContactId = ContentUris.parseId(rawContactUri); 2201 2202 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot2", "Tamale"); 2203 insertPhoneNumber(rawContactId, "861-0002"); 2204 2205 values.clear(); 2206 2207 // No match with international format 2208 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002"); 2209 assertEquals(0, getCount(lookupUri2, null, null)); 2210 2211 // No match with national format 2212 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002"); 2213 assertEquals(0, getCount(lookupUri2, null, null)); 2214 } 2215 2216 @Test testIntlPhoneLookupUseCases()2217 public void testIntlPhoneLookupUseCases() { 2218 // Checks the logic that relies on phone_number_compare_loose(Gingerbread) as a fallback 2219 //for phone number lookups. 2220 String fullNumber = "01197297427289"; 2221 2222 ContentValues values = new ContentValues(); 2223 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2224 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2225 long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values)); 2226 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 2227 insertPhoneNumber(rawContactId, fullNumber); 2228 2229 // Full number should definitely match. 2230 assertEquals(2, getCount(Uri.withAppendedPath( 2231 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null)); 2232 2233 // Shorter (local) number with 0 prefix should not match. 2234 assertEquals(0, getCount(Uri.withAppendedPath( 2235 PhoneLookup.CONTENT_FILTER_URI, "097427289"), null, null)); 2236 2237 // Number with international (+972) prefix should also match. 2238 assertEquals(1, getCount(Uri.withAppendedPath( 2239 PhoneLookup.CONTENT_FILTER_URI, "+97297427289"), null, null)); 2240 2241 // Same shorter number with dashes should not match. 2242 assertEquals(0, getCount(Uri.withAppendedPath( 2243 PhoneLookup.CONTENT_FILTER_URI, "09-742-7289"), null, null)); 2244 2245 // Same shorter number with spaces should not match. 2246 assertEquals(0, getCount(Uri.withAppendedPath( 2247 PhoneLookup.CONTENT_FILTER_URI, "09 742 7289"), null, null)); 2248 2249 // Some other number should not match. 2250 assertEquals(0, getCount(Uri.withAppendedPath( 2251 PhoneLookup.CONTENT_FILTER_URI, "049102395"), null, null)); 2252 } 2253 2254 @Test testPhoneLookupB5252190()2255 public void testPhoneLookupB5252190() { 2256 // Test cases from b/5252190 2257 String storedNumber = "796010101"; 2258 2259 ContentValues values = new ContentValues(); 2260 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2261 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2262 long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values)); 2263 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 2264 insertPhoneNumber(rawContactId, storedNumber); 2265 2266 assertEquals(1, getCount(Uri.withAppendedPath( 2267 PhoneLookup.CONTENT_FILTER_URI, "0796010101"), null, null)); 2268 2269 assertEquals(0, getCount(Uri.withAppendedPath( 2270 PhoneLookup.CONTENT_FILTER_URI, "+48796010101"), null, null)); 2271 2272 assertEquals(0, getCount(Uri.withAppendedPath( 2273 PhoneLookup.CONTENT_FILTER_URI, "48796010101"), null, null)); 2274 2275 assertEquals(0, getCount(Uri.withAppendedPath( 2276 PhoneLookup.CONTENT_FILTER_URI, "4-879-601-0101"), null, null)); 2277 2278 assertEquals(0, getCount(Uri.withAppendedPath( 2279 PhoneLookup.CONTENT_FILTER_URI, "4 879 601 0101"), null, null)); 2280 } 2281 2282 @Test testPhoneLookupUseStrictPhoneNumberCompare()2283 public void testPhoneLookupUseStrictPhoneNumberCompare() { 2284 // Test lookup cases when mUseStrictPhoneNumberComparison is true 2285 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 2286 final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest(); 2287 // Get and save the original value of mUseStrictPhoneNumberComparison so that we 2288 // can restore it when we are done with the test 2289 final boolean oldUseStrict = dbHelper.getUseStrictPhoneNumberComparisonForTest(); 2290 dbHelper.setUseStrictPhoneNumberComparisonForTest(true); 2291 2292 2293 try { 2294 String fullNumber = "01197297427289"; 2295 ContentValues values = new ContentValues(); 2296 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2297 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2298 long rawContactId = ContentUris.parseId( 2299 mResolver.insert(RawContacts.CONTENT_URI, values)); 2300 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 2301 insertPhoneNumber(rawContactId, fullNumber); 2302 insertPhoneNumber(rawContactId, "5103337596"); 2303 insertPhoneNumber(rawContactId, "+19012345678"); 2304 // One match for full number 2305 assertEquals(1, getCount(Uri.withAppendedPath( 2306 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null)); 2307 2308 // No matches for extra digit at the front 2309 assertEquals(0, getCount(Uri.withAppendedPath( 2310 PhoneLookup.CONTENT_FILTER_URI, "55103337596"), null, null)); 2311 // No matches for mispelled area code 2312 assertEquals(0, getCount(Uri.withAppendedPath( 2313 PhoneLookup.CONTENT_FILTER_URI, "5123337596"), null, null)); 2314 2315 // One match for matching number with dashes 2316 assertEquals(1, getCount(Uri.withAppendedPath( 2317 PhoneLookup.CONTENT_FILTER_URI, "510-333-7596"), null, null)); 2318 2319 // One match for matching number with international code 2320 assertEquals(1, getCount(Uri.withAppendedPath( 2321 PhoneLookup.CONTENT_FILTER_URI, "+1-510-333-7596"), null, null)); 2322 values.clear(); 2323 2324 // No matches for extra 0 in front 2325 assertEquals(0, getCount(Uri.withAppendedPath( 2326 PhoneLookup.CONTENT_FILTER_URI, "0-510-333-7596"), null, null)); 2327 values.clear(); 2328 2329 // No matches for different country code 2330 assertEquals(0, getCount(Uri.withAppendedPath( 2331 PhoneLookup.CONTENT_FILTER_URI, "+819012345678"), null, null)); 2332 values.clear(); 2333 } finally { 2334 // restore the original value of mUseStrictPhoneNumberComparison 2335 // upon test completion or failure 2336 dbHelper.setUseStrictPhoneNumberComparisonForTest(oldUseStrict); 2337 } 2338 } 2339 2340 /** 2341 * Test for enterprise caller-id, but with no corp profile. 2342 */ 2343 @Test testPhoneLookupEnterprise_noCorpProfile()2344 public void testPhoneLookupEnterprise_noCorpProfile() throws Exception { 2345 2346 Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111"); 2347 2348 // No contacts profile, no data. 2349 assertEquals(0, getCount(uri1)); 2350 2351 // Insert a contact into the primary CP2. 2352 long rawContactId = ContentUris.parseId( 2353 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2354 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 2355 insertPhoneNumber(rawContactId, "408-111-1111"); 2356 2357 // Do the query again and check the result. 2358 Cursor c = mResolver.query(uri1, null, null, null, null); 2359 try { 2360 assertEquals(1, c.getCount()); 2361 c.moveToPosition(0); 2362 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 2363 assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten. 2364 } finally { 2365 c.close(); 2366 } 2367 } 2368 2369 /** 2370 * Test for enterprise caller-id. Corp profile exists, but it returns a null cursor. 2371 */ 2372 @Test testPhoneLookupEnterprise_withCorpProfile_nullResult()2373 public void testPhoneLookupEnterprise_withCorpProfile_nullResult() throws Exception { 2374 setUpNullCorpProvider(); 2375 2376 Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111"); 2377 2378 // No contacts profile, no data. 2379 assertEquals(0, getCount(uri1)); 2380 2381 // Insert a contact into the primary CP2. 2382 long rawContactId = ContentUris.parseId( 2383 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2384 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 2385 insertPhoneNumber(rawContactId, "408-111-1111"); 2386 2387 // Do the query again and check the result. 2388 Cursor c = mResolver.query(uri1, null, null, null, null); 2389 try { 2390 assertEquals(1, c.getCount()); 2391 c.moveToPosition(0); 2392 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 2393 assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten. 2394 } finally { 2395 c.close(); 2396 } 2397 } 2398 2399 /** 2400 * Set up the corp user / CP2 and returns the corp CP2 instance. 2401 * 2402 * Create a second instance of CP2, and add it to the resolver, with the "user-id@" authority. 2403 */ setUpCorpProvider()2404 private SynchronousContactsProvider2 setUpCorpProvider() throws Exception { 2405 mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER); 2406 2407 // Note here we use a standalone CP2 so it'll have its own db helper. 2408 // Also use AlteringUserContext here to report the corp user id. 2409 final int userId = MockUserManager.CORP_USER.id; 2410 SynchronousContactsProvider2 provider = mActor.addProvider( 2411 new SecondaryUserContactsProvider2(userId), 2412 "" + userId + "@com.android.contacts", 2413 new AlteringUserContext(mActor.getProviderContext(), userId)); 2414 provider.wipeData(); 2415 return provider; 2416 } 2417 2418 /** 2419 * Similar to {@link #setUpCorpProvider}, but the corp CP2 set up with this will always return 2420 * null from query(). 2421 */ setUpNullCorpProvider()2422 private void setUpNullCorpProvider() throws Exception { 2423 mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER); 2424 2425 mActor.addProvider( 2426 NullContentProvider.class, 2427 "" + MockUserManager.CORP_USER.id + "@com.android.contacts", 2428 new AlteringUserContext(mActor.getProviderContext(), MockUserManager.CORP_USER.id)); 2429 } 2430 2431 /** 2432 * Test for query of merged primary and work contacts. 2433 * <p/> 2434 * Note: in this test, we add one more provider instance for the authority 2435 * "[email protected]" and use it as the corp cp2. 2436 */ 2437 @Test testQueryMergedDataPhones()2438 public void testQueryMergedDataPhones() throws Exception { 2439 mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS"); 2440 2441 // Insert a contact to the primary CP2. 2442 long rawContactId = ContentUris.parseId( 2443 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2444 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Primary"); 2445 2446 insertPhoneNumber(rawContactId, "111-111-1111", false, false, Phone.TYPE_MOBILE); 2447 2448 // Insert a contact to the corp CP2, with different name and phone number. 2449 final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider(); 2450 rawContactId = ContentUris.parseId( 2451 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 2452 // Insert a name. 2453 ContentValues cv = cv( 2454 Data.RAW_CONTACT_ID, rawContactId, 2455 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 2456 StructuredName.DISPLAY_NAME, "Contact2 Corp", 2457 StructuredName.GIVEN_NAME, "Contact2", 2458 StructuredName.FAMILY_NAME, "Corp"); 2459 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2460 // Insert a number. 2461 cv = cv( 2462 Data.RAW_CONTACT_ID, rawContactId, 2463 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 2464 Phone.NUMBER, "222-222-2222", 2465 Phone.TYPE, Phone.TYPE_MOBILE); 2466 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2467 2468 // Insert another contact to to corp CP2, with different name phone number and phone type 2469 rawContactId = ContentUris.parseId( 2470 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 2471 // Insert a name. 2472 cv = cv( 2473 Data.RAW_CONTACT_ID, rawContactId, 2474 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 2475 StructuredName.DISPLAY_NAME, "Contact3 Corp", 2476 StructuredName.GIVEN_NAME, "Contact3", 2477 StructuredName.FAMILY_NAME, "Corp"); 2478 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2479 // Insert a number 2480 cv = cv( 2481 Data.RAW_CONTACT_ID, rawContactId, 2482 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 2483 Phone.NUMBER, "333-333-3333", 2484 Phone.TYPE, Phone.TYPE_HOME); 2485 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2486 2487 // Execute the query to get the merged result. 2488 Cursor c = mResolver.query(Phone.ENTERPRISE_CONTENT_URI, new String[]{Phone.CONTACT_ID, 2489 Phone.DISPLAY_NAME, Phone.NUMBER}, Phone.TYPE + " = ?", 2490 new String[]{String.valueOf(Phone.TYPE_MOBILE)}, null); 2491 try { 2492 // Verify the primary contact. 2493 assertEquals(2, c.getCount()); 2494 assertEquals(3, c.getColumnCount()); 2495 c.moveToPosition(0); 2496 assertEquals("Contact1 Primary", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME))); 2497 assertEquals("111-111-1111", c.getString(c.getColumnIndex(Phone.NUMBER))); 2498 long contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID)); 2499 assertFalse(Contacts.isEnterpriseContactId(contactId)); 2500 2501 // Verify the enterprise contact. 2502 c.moveToPosition(1); 2503 assertEquals("Contact2 Corp", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME))); 2504 assertEquals("222-222-2222", c.getString(c.getColumnIndex(Phone.NUMBER))); 2505 contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID)); 2506 assertTrue(Contacts.isEnterpriseContactId(contactId)); 2507 } finally { 2508 c.close(); 2509 } 2510 } 2511 2512 /** 2513 * Test for query of merged primary and work contacts. 2514 * <p/> 2515 * Note: in this test, we add one more provider instance for the authority 2516 * "[email protected]" and use it as the corp cp2. 2517 */ 2518 @Test testQueryMergedDataPhones_nullCorp()2519 public void testQueryMergedDataPhones_nullCorp() throws Exception { 2520 mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS"); 2521 2522 // Insert a contact to the primary CP2. 2523 long rawContactId = ContentUris.parseId( 2524 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2525 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Primary"); 2526 2527 insertPhoneNumber(rawContactId, "111-111-1111", false, false, Phone.TYPE_MOBILE); 2528 2529 // Insert a contact to the corp CP2, with different name and phone number. 2530 setUpNullCorpProvider(); 2531 2532 // Execute the query to get the merged result. 2533 Cursor c = mResolver.query(Phone.ENTERPRISE_CONTENT_URI, new String[]{Phone.CONTACT_ID, 2534 Phone.DISPLAY_NAME, Phone.NUMBER}, Phone.TYPE + " = ?", 2535 new String[]{String.valueOf(Phone.TYPE_MOBILE)}, null); 2536 try { 2537 // Verify the primary contact. 2538 assertEquals(1, c.getCount()); 2539 assertEquals(3, c.getColumnCount()); 2540 c.moveToPosition(0); 2541 assertEquals("Contact1 Primary", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME))); 2542 assertEquals("111-111-1111", c.getString(c.getColumnIndex(Phone.NUMBER))); 2543 long contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID)); 2544 assertFalse(Contacts.isEnterpriseContactId(contactId)); 2545 } finally { 2546 c.close(); 2547 } 2548 } 2549 2550 /** 2551 * Test for enterprise caller-id, with the corp profile. 2552 * 2553 * Note: in this test, we add one more provider instance for the authority 2554 * "[email protected]" and use it as the corp cp2. 2555 */ 2556 @Test testPhoneLookupEnterprise_withCorpProfile()2557 public void testPhoneLookupEnterprise_withCorpProfile() throws Exception { 2558 final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider(); 2559 2560 Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111"); 2561 Uri uri2 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222"); 2562 2563 // First, test with no contacts on either profile. 2564 assertEquals(0, getCount(uri1)); 2565 2566 // Insert a contact to the primary CP2. 2567 long rawContactId = ContentUris.parseId( 2568 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2569 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 2570 insertPhoneNumber(rawContactId, "408-111-1111"); 2571 2572 // Insert a contact to the corp CP2, with the same phone number, but with a different name. 2573 rawContactId = ContentUris.parseId( 2574 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 2575 // Insert a name 2576 ContentValues cv = cv( 2577 Data.RAW_CONTACT_ID, rawContactId, 2578 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 2579 StructuredName.DISPLAY_NAME, "Contact2 Corp", 2580 StructuredName.GIVEN_NAME, "Contact2", 2581 StructuredName.FAMILY_NAME, "Corp"); 2582 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2583 2584 // Insert a number 2585 cv = cv( 2586 Data.RAW_CONTACT_ID, rawContactId, 2587 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 2588 Phone.NUMBER, "408-111-1111", 2589 Phone.TYPE, Phone.TYPE_HOME); 2590 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2591 2592 // Insert one more contact to the corp CP2, with a different number. 2593 rawContactId = ContentUris.parseId( 2594 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 2595 // Insert a name 2596 cv = cv( 2597 Data.RAW_CONTACT_ID, rawContactId, 2598 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 2599 StructuredName.DISPLAY_NAME, "Contact3 Corp", 2600 StructuredName.GIVEN_NAME, "Contact3", 2601 StructuredName.FAMILY_NAME, "Corp"); 2602 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2603 2604 // Insert a number 2605 cv = cv( 2606 Data.RAW_CONTACT_ID, rawContactId, 2607 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 2608 Phone.NUMBER, "408-222-2222", 2609 Phone.TYPE, Phone.TYPE_HOME); 2610 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2611 2612 // Okay, now execute queries and check the result. 2613 2614 // The first URL hits the contact in the primary CP2. 2615 // There's also a contact with this phone number in the corp CP2, but that will be ignored. 2616 Cursor c = mResolver.query(uri1, null, null, null, null); 2617 try { 2618 assertEquals(1, c.getCount()); 2619 c.moveToPosition(0); 2620 assertEquals("Contact1 Doe", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME))); 2621 2622 // Make sure it has a personal contact ID. 2623 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 2624 assertFalse(Contacts.isEnterpriseContactId(contactId)); 2625 } finally { 2626 c.close(); 2627 } 2628 2629 // Test for the second phone number, which only exists in the corp cp2. 2630 c = mResolver.query(uri2, null, null, null, null); 2631 try { 2632 // This one actually returns 2 identical rows, probably because of the join 2633 // in phone_lookup. Callers only care the first row, so returning multiple identical 2634 // rows should be fine. 2635 assertTrue(c.getCount() > 0); 2636 c.moveToPosition(0); 2637 assertEquals("Contact3 Corp", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME))); 2638 2639 // Make sure it has a corp contact ID. 2640 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 2641 assertTrue(Contacts.isEnterpriseContactId(contactId)); 2642 } finally { 2643 c.close(); 2644 } 2645 } 2646 2647 @Test testQueryRawContactEntitiesCorp_noCorpProfile()2648 public void testQueryRawContactEntitiesCorp_noCorpProfile() { 2649 mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS"); 2650 2651 // Insert a contact into the primary CP2. 2652 long rawContactId = ContentUris.parseId( 2653 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2654 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 2655 insertPhoneNumber(rawContactId, "408-111-1111"); 2656 2657 // No corp profile, no data. 2658 assertEquals(0, getCount(RawContactsEntity.CORP_CONTENT_URI)); 2659 } 2660 2661 @Test testQueryRawContactEntitiesCorp_withCorpProfile()2662 public void testQueryRawContactEntitiesCorp_withCorpProfile() throws Exception { 2663 mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS"); 2664 2665 // Insert a contact into the primary CP2. 2666 long rawContactId = ContentUris.parseId( 2667 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 2668 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 2669 insertPhoneNumber(rawContactId, "408-111-1111"); 2670 2671 // Insert a contact into corp CP2. 2672 final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider(); 2673 rawContactId = ContentUris.parseId( 2674 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 2675 // Insert a name. 2676 ContentValues cv = cv( 2677 Data.RAW_CONTACT_ID, rawContactId, 2678 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 2679 StructuredName.DISPLAY_NAME, "Contact2 Corp"); 2680 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2681 // Insert a number. 2682 cv = cv( 2683 Data.RAW_CONTACT_ID, rawContactId, 2684 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 2685 Phone.NUMBER, "222-222-2222", 2686 Phone.TYPE, Phone.TYPE_MOBILE); 2687 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 2688 2689 // Do the query 2690 Cursor c = mResolver.query(RawContactsEntity.CORP_CONTENT_URI, 2691 new String[]{RawContactsEntity._ID, RawContactsEntity.DATA1}, 2692 RawContactsEntity.MIMETYPE + "=?", new String[]{ 2693 StructuredName.CONTENT_ITEM_TYPE}, null); 2694 // The result should only contains corp data. 2695 assertEquals(1, c.getCount()); 2696 assertEquals(2, c.getColumnCount()); 2697 c.moveToPosition(0); 2698 long id = c.getLong(c.getColumnIndex(RawContactsEntity._ID)); 2699 String data1 = c.getString(c.getColumnIndex(RawContactsEntity.DATA1)); 2700 assertEquals("Contact2 Corp", data1); 2701 assertEquals(rawContactId, id); 2702 c.close(); 2703 } 2704 2705 @Test testRewriteCorpDirectories()2706 public void testRewriteCorpDirectories() { 2707 // 6 columns 2708 final MatrixCursor c = new MatrixCursor(new String[] { 2709 Directory._ID, 2710 Directory.PACKAGE_NAME, 2711 Directory.TYPE_RESOURCE_ID, 2712 Directory.DISPLAY_NAME, 2713 Directory.ACCOUNT_TYPE, 2714 Directory.ACCOUNT_NAME, 2715 }); 2716 2717 // First, convert and make sure it returns an empty cursor. 2718 Cursor rewritten = ContactsProvider2.rewriteCorpDirectories(c); 2719 2720 assertEquals(0, rewritten.getCount()); 2721 assertEquals(6, rewritten.getColumnCount()); 2722 2723 c.addRow(new Object[] { 2724 5L, // Directory._ID 2725 "name", // Directory.PACKAGE_NAME 2726 123, // Directory.TYPE_RESOURCE_ID 2727 "display", // Directory.DISPLAY_NAME 2728 "atype", // Directory.ACCOUNT_TYPE 2729 "aname", // Directory.ACCOUNT_NAME 2730 }); 2731 2732 rewritten = ContactsProvider2.rewriteCorpDirectories(c); 2733 assertEquals(1, rewritten.getCount()); 2734 assertEquals(6, rewritten.getColumnCount()); 2735 2736 rewritten.moveToPosition(0); 2737 int column = 0; 2738 assertEquals(1000000005L, rewritten.getLong(column++)); 2739 assertEquals("name", rewritten.getString(column++)); 2740 assertEquals(123, rewritten.getInt(column++)); 2741 assertEquals("display", rewritten.getString(column++)); 2742 assertEquals("atype", rewritten.getString(column++)); 2743 assertEquals("aname", rewritten.getString(column++)); 2744 } 2745 2746 @Test testPhoneUpdate()2747 public void testPhoneUpdate() { 2748 ContentValues values = new ContentValues(); 2749 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2750 long rawContactId = ContentUris.parseId(rawContactUri); 2751 2752 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 2753 Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411"); 2754 2755 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 2756 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422"); 2757 assertEquals(2, getCount(lookupUri1, null, null)); 2758 assertEquals(0, getCount(lookupUri2, null, null)); 2759 2760 values.clear(); 2761 values.put(Phone.NUMBER, "18004664422"); 2762 mResolver.update(phoneUri, values, null, null); 2763 2764 assertEquals(0, getCount(lookupUri1, null, null)); 2765 assertEquals(2, getCount(lookupUri2, null, null)); 2766 2767 // Setting number to null will remove the phone lookup record 2768 values.clear(); 2769 values.putNull(Phone.NUMBER); 2770 mResolver.update(phoneUri, values, null, null); 2771 2772 assertEquals(0, getCount(lookupUri1, null, null)); 2773 assertEquals(0, getCount(lookupUri2, null, null)); 2774 2775 // Let's restore that phone lookup record 2776 values.clear(); 2777 values.put(Phone.NUMBER, "18004664422"); 2778 mResolver.update(phoneUri, values, null, null); 2779 assertEquals(0, getCount(lookupUri1, null, null)); 2780 assertEquals(2, getCount(lookupUri2, null, null)); 2781 assertNetworkNotified(true); 2782 } 2783 2784 /** Tests if {@link Callable#CONTENT_URI} returns both phones and sip addresses. */ 2785 @Test testCallablesQuery()2786 public void testCallablesQuery() { 2787 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Meghan", "Knox"); 2788 long phoneId1 = ContentUris.parseId(insertPhoneNumber(rawContactId1, "18004664411")); 2789 long contactId1 = queryContactId(rawContactId1); 2790 2791 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 2792 long sipAddressId2 = ContentUris.parseId( 2793 insertSipAddress(rawContactId2, "[email protected]")); 2794 long contactId2 = queryContactId(rawContactId2); 2795 2796 ContentValues values1 = new ContentValues(); 2797 values1.put(Data._ID, phoneId1); 2798 values1.put(Data.RAW_CONTACT_ID, rawContactId1); 2799 values1.put(RawContacts.CONTACT_ID, contactId1); 2800 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 2801 values1.put(Phone.NUMBER, "18004664411"); 2802 values1.put(Phone.TYPE, Phone.TYPE_HOME); 2803 values1.putNull(Phone.LABEL); 2804 values1.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 2805 2806 ContentValues values2 = new ContentValues(); 2807 values2.put(Data._ID, sipAddressId2); 2808 values2.put(Data.RAW_CONTACT_ID, rawContactId2); 2809 values2.put(RawContacts.CONTACT_ID, contactId2); 2810 values2.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 2811 values2.put(SipAddress.SIP_ADDRESS, "[email protected]"); 2812 values2.put(Contacts.DISPLAY_NAME, "John Doe"); 2813 2814 assertEquals(2, getCount(Callable.CONTENT_URI, null, null)); 2815 assertStoredValues(Callable.CONTENT_URI, new ContentValues[] { values1, values2 }); 2816 } 2817 2818 @Test testCallablesFilterQuery()2819 public void testCallablesFilterQuery() { 2820 testPhonesFilterQueryInter(Callable.CONTENT_FILTER_URI); 2821 } 2822 2823 @Test testEmailsQuery()2824 public void testEmailsQuery() { 2825 ContentValues values = new ContentValues(); 2826 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2827 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2828 values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 5); 2829 values.put(RawContacts.TIMES_CONTACTED, 54321); 2830 values.put(RawContacts.STARRED, 1); 2831 2832 Uri rawContactUri = insertRawContact(values); 2833 final long rawContactId = ContentUris.parseId(rawContactUri); 2834 2835 DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox"); 2836 final Uri emailUri = insertEmail(rawContactId, "[email protected]"); 2837 final long emailId = ContentUris.parseId(emailUri); 2838 2839 final long contactId = queryContactId(rawContactId); 2840 values.clear(); 2841 values.put(Data._ID, emailId); 2842 values.put(Data.RAW_CONTACT_ID, rawContactId); 2843 values.put(RawContacts.CONTACT_ID, contactId); 2844 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2845 values.put(Email.DATA, "[email protected]"); 2846 values.put(Email.TYPE, Email.TYPE_HOME); 2847 values.putNull(Email.LABEL); 2848 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 2849 values.put(Contacts.CUSTOM_RINGTONE, "d"); 2850 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 2851 values.put(Contacts.LAST_TIME_CONTACTED, 0); 2852 values.put(Contacts.TIMES_CONTACTED, 0); 2853 values.put(Contacts.STARRED, 1); 2854 2855 assertStoredValues(Email.CONTENT_URI, values); 2856 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 2857 2858 // Check if the provider detects duplicated email addresses. 2859 final Uri emailUri2 = insertEmail(rawContactId, "[email protected]"); 2860 final long emailId2 = ContentUris.parseId(emailUri2); 2861 final ContentValues values2 = new ContentValues(values); 2862 values2.put(Data._ID, emailId2); 2863 2864 final Uri dedupeUri = Email.CONTENT_URI.buildUpon() 2865 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 2866 .build(); 2867 2868 // URI with ID should return a correct result. 2869 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 2870 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values); 2871 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2); 2872 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2); 2873 2874 assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2}); 2875 2876 // If requested to remove duplicates, the query should return just one result, 2877 // whose _ID won't be deterministic. 2878 values.remove(Data._ID); 2879 assertStoredValues(dedupeUri, values); 2880 } 2881 2882 @Test testEmailsLookupQuery()2883 public void testEmailsLookupQuery() { 2884 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale"); 2885 insertEmail(rawContactId, "[email protected]"); 2886 2887 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "[email protected]"); 2888 ContentValues values = new ContentValues(); 2889 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2890 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2891 values.put(Email.DATA, "[email protected]"); 2892 values.put(Email.TYPE, Email.TYPE_HOME); 2893 values.putNull(Email.LABEL); 2894 assertStoredValues(filterUri1, values); 2895 2896 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<[email protected]>"); 2897 assertStoredValues(filterUri2, values); 2898 2899 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "[email protected]"); 2900 assertEquals(0, getCount(filterUri3, null, null)); 2901 } 2902 2903 @Test testEmailsFilterQuery()2904 public void testEmailsFilterQuery() { 2905 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale", 2906 TestUtil.ACCOUNT_1); 2907 insertEmail(rawContactId1, "[email protected]"); 2908 insertEmail(rawContactId1, "[email protected]"); 2909 2910 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale", 2911 TestUtil.ACCOUNT_2); 2912 insertEmail(rawContactId2, "[email protected]"); 2913 2914 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam"); 2915 ContentValues values = new ContentValues(); 2916 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2917 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2918 values.put(Email.DATA, "[email protected]"); 2919 values.put(Email.TYPE, Email.TYPE_HOME); 2920 values.putNull(Email.LABEL); 2921 assertStoredValuesWithProjection(filterUri1, values); 2922 2923 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot"); 2924 assertStoredValuesWithProjection(filterUri2, values); 2925 2926 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale"); 2927 assertStoredValuesWithProjection(filterUri3, values); 2928 2929 Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme"); 2930 assertStoredValuesWithProjection(filterUri4, values); 2931 2932 Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada"); 2933 assertEquals(0, getCount(filterUri5, null, null)); 2934 } 2935 2936 /** 2937 * Tests if ContactsProvider2 returns addresses according to registration order. 2938 */ 2939 @Test testEmailFilterDefaultSortOrder()2940 public void testEmailFilterDefaultSortOrder() { 2941 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2942 insertEmail(rawContactId1, "[email protected]"); 2943 insertEmail(rawContactId1, "[email protected]"); 2944 insertEmail(rawContactId1, "[email protected]"); 2945 ContentValues v1 = new ContentValues(); 2946 v1.put(Email.ADDRESS, "[email protected]"); 2947 ContentValues v2 = new ContentValues(); 2948 v2.put(Email.ADDRESS, "[email protected]"); 2949 ContentValues v3 = new ContentValues(); 2950 v3.put(Email.ADDRESS, "[email protected]"); 2951 2952 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2953 assertStoredValuesOrderly(filterUri, new ContentValues[]{v1, v2, v3}); 2954 } 2955 2956 /** 2957 * Tests if ContactsProvider2 returns primary addresses before the other addresses. 2958 */ 2959 @Test testEmailFilterPrimaryAddress()2960 public void testEmailFilterPrimaryAddress() { 2961 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2962 insertEmail(rawContactId1, "[email protected]"); 2963 insertEmail(rawContactId1, "[email protected]", true); 2964 ContentValues v1 = new ContentValues(); 2965 v1.put(Email.ADDRESS, "[email protected]"); 2966 ContentValues v2 = new ContentValues(); 2967 v2.put(Email.ADDRESS, "[email protected]"); 2968 2969 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2970 assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 }); 2971 } 2972 2973 /** 2974 * Tests if ContactsProvider2 has email address associated with a primary account before the 2975 * other address. 2976 */ 2977 @Test testEmailFilterPrimaryAccount()2978 public void testEmailFilterPrimaryAccount() { 2979 long rawContactId1 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 2980 insertEmail(rawContactId1, "[email protected]"); 2981 long rawContactId2 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_2); 2982 insertEmail(rawContactId2, "[email protected]"); 2983 ContentValues v1 = new ContentValues(); 2984 v1.put(Email.ADDRESS, "[email protected]"); 2985 ContentValues v2 = new ContentValues(); 2986 v2.put(Email.ADDRESS, "[email protected]"); 2987 2988 Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2989 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name) 2990 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_1.type) 2991 .build(); 2992 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 }); 2993 2994 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2995 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name) 2996 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_2.type) 2997 .build(); 2998 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 }); 2999 3000 // Just with PRIMARY_ACCOUNT_NAME 3001 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 3002 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name) 3003 .build(); 3004 assertStoredValuesOrderly(filterUri3, new ContentValues[]{v1, v2}); 3005 3006 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 3007 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name) 3008 .build(); 3009 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 }); 3010 } 3011 3012 /** 3013 * Test emails with the same domain as primary account are ordered first. 3014 */ 3015 @Test testEmailFilterSameDomainAccountOrder()3016 public void testEmailFilterSameDomainAccountOrder() { 3017 final Account account = new Account("[email protected]", "not_used"); 3018 final long rawContactId = RawContactUtil.createRawContact(mResolver, account); 3019 insertEmail(rawContactId, "[email protected]"); 3020 insertEmail(rawContactId, "[email protected]"); 3021 3022 final ContentValues v1 = cv(Email.ADDRESS, "[email protected]"); 3023 final ContentValues v2 = cv(Email.ADDRESS, "[email protected]"); 3024 3025 Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 3026 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, account.name) 3027 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, account.type) 3028 .build(); 3029 assertStoredValuesOrderly(filterUri1, v2, v1); 3030 } 3031 3032 /** 3033 * Test "default" emails are sorted above emails used last. 3034 */ 3035 @Test testEmailFilterSuperPrimaryOverUsageSort()3036 public void testEmailFilterSuperPrimaryOverUsageSort() { 3037 final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 3038 final Uri emailUri1 = insertEmail(rawContactId, "[email protected]"); 3039 final Uri emailUri2 = insertEmail(rawContactId, "[email protected]"); 3040 insertEmail(rawContactId, "[email protected]", true, true); 3041 3042 // Update account1 and account 2 to have higher usage. 3043 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 3044 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 3045 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2); 3046 3047 final ContentValues v1 = cv(Email.ADDRESS, "[email protected]"); 3048 final ContentValues v2 = cv(Email.ADDRESS, "[email protected]"); 3049 final ContentValues v3 = cv(Email.ADDRESS, "[email protected]"); 3050 3051 // Test that account 3 is first even though account 1 and 2 have higher usage. 3052 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc"); 3053 assertStoredValuesOrderly(filterUri, v3, v1, v2); 3054 } 3055 3056 @Test testEmailFilterUsageOverPrimarySort()3057 public void testEmailFilterUsageOverPrimarySort() { 3058 final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 3059 final Uri emailUri1 = insertEmail(rawContactId, "[email protected]"); 3060 final Uri emailUri2 = insertEmail(rawContactId, "[email protected]"); 3061 insertEmail(rawContactId, "[email protected]", true); 3062 3063 // Update account1 and account 2 to have higher usage. 3064 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 3065 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 3066 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2); 3067 3068 final ContentValues v1 = cv(Email.ADDRESS, "[email protected]"); 3069 final ContentValues v2 = cv(Email.ADDRESS, "[email protected]"); 3070 final ContentValues v3 = cv(Email.ADDRESS, "[email protected]"); 3071 3072 // No usage stats any more, so v3 is still the first. 3073 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc"); 3074 assertStoredValuesOrderly(filterUri, v3, v1, v2); 3075 } 3076 3077 /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */ 3078 @Test testEmailFilterSortOrderWithFeedback()3079 public void testEmailFilterSortOrderWithFeedback() { 3080 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 3081 String address1 = "[email protected]"; 3082 insertEmail(rawContactId1, address1); 3083 3084 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 3085 String address2 = "[email protected]"; 3086 insertEmail(rawContactId2, address2); 3087 String address3 = "[email protected]"; 3088 ContentUris.parseId(insertEmail(rawContactId2, address3)); 3089 3090 ContentValues v1 = new ContentValues(); 3091 v1.put(Email.ADDRESS, "[email protected]"); 3092 ContentValues v2 = new ContentValues(); 3093 v2.put(Email.ADDRESS, "[email protected]"); 3094 ContentValues v3 = new ContentValues(); 3095 v3.put(Email.ADDRESS, "[email protected]"); 3096 3097 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 3098 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 3099 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 3100 DataUsageFeedback.USAGE_TYPE_CALL) 3101 .build(); 3102 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 3103 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 3104 DataUsageFeedback.USAGE_TYPE_LONG_TEXT) 3105 .build(); 3106 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 3107 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 3108 DataUsageFeedback.USAGE_TYPE_SHORT_TEXT) 3109 .build(); 3110 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 }); 3111 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 }); 3112 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 }); 3113 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 }); 3114 3115 sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3); 3116 3117 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 3118 cv(RawContacts._ID, rawContactId1, 3119 RawContacts.TIMES_CONTACTED, 0 3120 ), 3121 cv(RawContacts._ID, rawContactId2, 3122 RawContacts.TIMES_CONTACTED, 0 3123 ) 3124 ); 3125 3126 // No more interaction counter, so the order doesn't change. 3127 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 }); 3128 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 }); 3129 } 3130 3131 @Test testAddQueryParametersFromUri()3132 public void testAddQueryParametersFromUri() { 3133 final ContactsProvider2 provider = (ContactsProvider2) getProvider(); 3134 final Uri originalUri = Phone.CONTENT_FILTER_URI.buildUpon() 3135 .appendQueryParameter("a", "a") 3136 .appendQueryParameter("b", "b") 3137 .appendQueryParameter("c", "c").build(); 3138 final Uri.Builder targetBuilder = Phone.CONTENT_FILTER_URI.buildUpon(); 3139 provider.addQueryParametersFromUri(targetBuilder, originalUri, 3140 new ArraySet<String>(Arrays.asList(new String[] { 3141 "b" 3142 }))); 3143 final Uri targetUri = targetBuilder.build(); 3144 assertEquals(1, targetUri.getQueryParameters("a").size()); 3145 assertEquals(0, targetUri.getQueryParameters("b").size()); 3146 assertEquals(1, targetUri.getQueryParameters("c").size()); 3147 } 3148 buildContactsFilterUriWithDirectory(String directory)3149 private Uri buildContactsFilterUriWithDirectory(String directory) { 3150 return Contacts.CONTENT_FILTER_URI.buildUpon() 3151 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory).build(); 3152 } 3153 3154 @Test testTestInvalidDirectory()3155 public void testTestInvalidDirectory() throws Exception { 3156 final ContactsProvider2 provider = (ContactsProvider2) getProvider(); 3157 assertTrue(provider.isDirectoryParamValid(Contacts.CONTENT_FILTER_URI)); 3158 assertFalse(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory(""))); 3159 assertTrue(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("0"))); 3160 assertTrue(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("123"))); 3161 assertFalse(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("abc"))); 3162 } 3163 3164 @Test testQueryCorpContactsProvider()3165 public void testQueryCorpContactsProvider() throws Exception { 3166 final ContactsProvider2 provider = (ContactsProvider2) getProvider(); 3167 final MockUserManager um = mActor.mockUserManager; 3168 final Uri enterpriseUri = 3169 Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222"); 3170 final Uri invalidAuthorityUri = android.provider.Settings.Secure.CONTENT_URI; 3171 3172 // No corp user. Primary only. 3173 assertEquals(-1, UserUtils.getCorpUserId(mActor.getProviderContext())); 3174 assertEquals(0, provider.queryCorpContactsProvider(enterpriseUri, null, null, null, 3175 null, null).getCount()); 3176 3177 final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider(); 3178 // Insert a contact to the corp CP2 3179 long rawContactId = ContentUris.parseId( 3180 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 3181 // Insert a name 3182 ContentValues cv = cv( 3183 Data.RAW_CONTACT_ID, rawContactId, 3184 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 3185 StructuredName.DISPLAY_NAME, "Contact2 Corp", 3186 StructuredName.GIVEN_NAME, "Contact2", 3187 StructuredName.FAMILY_NAME, "Corp"); 3188 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 3189 // Insert a number 3190 cv = cv( 3191 Data.RAW_CONTACT_ID, rawContactId, 3192 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 3193 Phone.NUMBER, "408-222-2222", 3194 Phone.TYPE, Phone.TYPE_HOME); 3195 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 3196 // Primary + corp 3197 um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER); 3198 // It returns 2 identical rows, probably because of the join in phone_lookup. 3199 assertEquals(2, provider.queryCorpContactsProvider(enterpriseUri, null, null, null, 3200 null, null).getCount()); 3201 try { 3202 provider.queryCorpContactsProvider(invalidAuthorityUri, null, null, 3203 null, null, null); 3204 fail(invalidAuthorityUri.toString() + " should throw IllegalArgumentException"); 3205 } catch (IllegalArgumentException e) { 3206 // Expected 3207 } 3208 } 3209 3210 @Test testPostalsQuery()3211 public void testPostalsQuery() { 3212 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore"); 3213 Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 3214 final long dataId = ContentUris.parseId(dataUri); 3215 3216 final long contactId = queryContactId(rawContactId); 3217 ContentValues values = new ContentValues(); 3218 values.put(Data._ID, dataId); 3219 values.put(Data.RAW_CONTACT_ID, rawContactId); 3220 values.put(RawContacts.CONTACT_ID, contactId); 3221 values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 3222 values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View"); 3223 values.put(Contacts.DISPLAY_NAME, "Alice Nextore"); 3224 3225 assertStoredValues(StructuredPostal.CONTENT_URI, values); 3226 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 3227 values); 3228 assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId); 3229 3230 // Check if the provider detects duplicated addresses. 3231 Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 3232 final long dataId2 = ContentUris.parseId(dataUri2); 3233 final ContentValues values2 = new ContentValues(values); 3234 values2.put(Data._ID, dataId2); 3235 3236 final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon() 3237 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 3238 .build(); 3239 3240 // URI with ID should return a correct result. 3241 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 3242 values); 3243 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values); 3244 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2), 3245 values2); 3246 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2); 3247 3248 assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2}); 3249 3250 // If requested to remove duplicates, the query should return just one result, 3251 // whose _ID won't be deterministic. 3252 values.remove(Data._ID); 3253 assertStoredValues(dedupeUri, values); 3254 } 3255 3256 @Test testDataContentUriInvisibleQuery()3257 public void testDataContentUriInvisibleQuery() { 3258 final ContentValues values = new ContentValues(); 3259 final long contactId = createContact(values, "John", "Doe", 3260 "18004664411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3261 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 3262 3263 final Uri uri = Data.CONTENT_URI.buildUpon(). 3264 appendQueryParameter(Data.VISIBLE_CONTACTS_ONLY, "true").build(); 3265 assertEquals(4, getCount(uri, null, null)); 3266 3267 markInvisible(contactId); 3268 3269 assertEquals(0, getCount(uri, null, null)); 3270 } 3271 3272 @Test testInDefaultDirectoryData()3273 public void testInDefaultDirectoryData() { 3274 final ContentValues values = new ContentValues(); 3275 final long contactId = createContact(values, "John", "Doe", 3276 "18004664411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3277 StatusUpdates.CAPABILITY_HAS_CAMERA); 3278 3279 final StringBuilder query = new StringBuilder() 3280 .append(Data.MIMETYPE).append("='").append(Email.CONTENT_ITEM_TYPE) 3281 .append("' AND ").append(Email.DATA).append("=? AND ") 3282 .append(Contacts.IN_DEFAULT_DIRECTORY).append("=1"); 3283 3284 assertEquals(1, 3285 getCount(Email.CONTENT_URI, query.toString(), new String[]{"[email protected]"})); 3286 3287 // Fire! 3288 markInvisible(contactId); 3289 3290 // Verify: making a contact visible changes the IN_DEFAULT_DIRECTORY data value. 3291 assertEquals(0, 3292 getCount(Email.CONTENT_URI, query.toString(), new String[]{"[email protected]"})); 3293 } 3294 3295 @Test testContactablesQuery()3296 public void testContactablesQuery() { 3297 final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", 3298 "Tamale"); 3299 3300 insertPhoneNumber(rawContactId, "510-123-5769"); 3301 insertEmail(rawContactId, "[email protected]"); 3302 3303 final ContentValues cv1 = new ContentValues(); 3304 cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 3305 cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3306 cv1.put(Email.DATA, "[email protected]"); 3307 cv1.put(Email.TYPE, Email.TYPE_HOME); 3308 cv1.putNull(Email.LABEL); 3309 3310 final ContentValues cv2 = new ContentValues(); 3311 cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 3312 cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 3313 cv2.put(Phone.DATA, "510-123-5769"); 3314 cv2.put(Phone.TYPE, Phone.TYPE_HOME); 3315 cv2.putNull(Phone.LABEL); 3316 3317 final Uri filterUri0 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, ""); 3318 assertEquals(0, getCount(filterUri0, null, null)); 3319 3320 final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale"); 3321 assertStoredValues(filterUri1, cv1, cv2); 3322 3323 final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot"); 3324 assertStoredValues(filterUri2, cv1, cv2); 3325 3326 final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale@ac"); 3327 assertStoredValues(filterUri3, cv1, cv2); 3328 3329 final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "510"); 3330 assertStoredValues(filterUri4, cv1, cv2); 3331 3332 final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "cold"); 3333 assertEquals(0, getCount(filterUri5, null, null)); 3334 3335 final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, 3336 "tamale@google"); 3337 assertEquals(0, getCount(filterUri6, null, null)); 3338 3339 final Uri filterUri7 = Contactables.CONTENT_URI; 3340 assertStoredValues(filterUri7, cv1, cv2); 3341 } 3342 3343 @Test testContactablesMultipleQuery()3344 public void testContactablesMultipleQuery() { 3345 3346 final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", 3347 "Tamale"); 3348 insertPhoneNumber(rawContactId, "510-123-5769"); 3349 insertEmail(rawContactId, "[email protected]"); 3350 insertEmail(rawContactId, "[email protected]"); 3351 3352 final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Cold", 3353 "Tamago"); 3354 insertEmail(rawContactId2, "[email protected]"); 3355 3356 final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 3357 insertPhoneNumber(rawContactId3, "518-354-1111"); 3358 insertEmail(rawContactId3, "[email protected]"); 3359 3360 final ContentValues cv1 = new ContentValues(); 3361 cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 3362 cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3363 cv1.put(Email.DATA, "[email protected]"); 3364 cv1.put(Email.TYPE, Email.TYPE_HOME); 3365 cv1.putNull(Email.LABEL); 3366 3367 final ContentValues cv2 = new ContentValues(); 3368 cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 3369 cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 3370 cv2.put(Phone.DATA, "510-123-5769"); 3371 cv2.put(Phone.TYPE, Phone.TYPE_HOME); 3372 cv2.putNull(Phone.LABEL); 3373 3374 final ContentValues cv3 = new ContentValues(); 3375 cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 3376 cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3377 cv3.put(Email.DATA, "[email protected]"); 3378 cv3.put(Email.TYPE, Email.TYPE_HOME); 3379 cv3.putNull(Email.LABEL); 3380 3381 final ContentValues cv4 = new ContentValues(); 3382 cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago"); 3383 cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3384 cv4.put(Email.DATA, "[email protected]"); 3385 cv4.put(Email.TYPE, Email.TYPE_HOME); 3386 cv4.putNull(Email.LABEL); 3387 3388 final ContentValues cv5 = new ContentValues(); 3389 cv5.put(Contacts.DISPLAY_NAME, "John Doe"); 3390 cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3391 cv5.put(Email.DATA, "[email protected]"); 3392 cv5.put(Email.TYPE, Email.TYPE_HOME); 3393 cv5.putNull(Email.LABEL); 3394 3395 final ContentValues cv6 = new ContentValues(); 3396 cv6.put(Contacts.DISPLAY_NAME, "John Doe"); 3397 cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 3398 cv6.put(Phone.DATA, "518-354-1111"); 3399 cv6.put(Phone.TYPE, Phone.TYPE_HOME); 3400 cv6.putNull(Phone.LABEL); 3401 3402 final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale"); 3403 3404 assertStoredValues(filterUri1, cv1, cv2, cv3); 3405 3406 final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot"); 3407 assertStoredValues(filterUri2, cv1, cv2, cv3); 3408 3409 final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam"); 3410 assertStoredValues(filterUri3, cv1, cv2, cv3, cv4); 3411 3412 final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518"); 3413 assertStoredValues(filterUri4, cv5, cv6); 3414 3415 final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doe"); 3416 assertStoredValues(filterUri5, cv5, cv6); 3417 3418 final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51"); 3419 assertStoredValues(filterUri6, cv1, cv2, cv3, cv5, cv6); 3420 3421 final Uri filterUri7 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, 3422 "tamale@google"); 3423 assertEquals(0, getCount(filterUri7, null, null)); 3424 3425 final Uri filterUri8 = Contactables.CONTENT_URI; 3426 assertStoredValues(filterUri8, cv1, cv2, cv3, cv4, cv5, cv6); 3427 3428 // test VISIBLE_CONTACTS_ONLY boolean parameter 3429 final Uri filterUri9 = filterUri6.buildUpon().appendQueryParameter( 3430 Contactables.VISIBLE_CONTACTS_ONLY, "true").build(); 3431 assertStoredValues(filterUri9, cv1, cv2, cv3, cv5, cv6); 3432 // mark Hot Tamale as invisible - cv1, cv2, and cv3 should no longer be in the cursor 3433 markInvisible(queryContactId(rawContactId)); 3434 assertStoredValues(filterUri9, cv5, cv6); 3435 } 3436 3437 3438 @Test testQueryContactData()3439 public void testQueryContactData() { 3440 ContentValues values = new ContentValues(); 3441 long contactId = createContact(values, "John", "Doe", 3442 "18004664411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3443 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 3444 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3445 3446 values.put(Contacts.TIMES_CONTACTED, 0); 3447 assertStoredValues(contactUri, values); 3448 } 3449 3450 @Test testQueryContactWithStatusUpdate()3451 public void testQueryContactWithStatusUpdate() { 3452 ContentValues values = new ContentValues(); 3453 long contactId = createContact(values, "John", "Doe", 3454 "18004664411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3455 StatusUpdates.CAPABILITY_HAS_CAMERA); 3456 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 3457 values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 3458 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3459 3460 values.put(Contacts.TIMES_CONTACTED, 0); 3461 3462 assertStoredValuesWithProjection(contactUri, values); 3463 } 3464 3465 @Test testQueryContactFilterByName()3466 public void testQueryContactFilterByName() { 3467 ContentValues values = new ContentValues(); 3468 long rawContactId = createRawContact(values, "18004664411", 3469 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3470 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 3471 StatusUpdates.CAPABILITY_HAS_VOICE); 3472 3473 ContentValues nameValues = new ContentValues(); 3474 nameValues.put(StructuredName.GIVEN_NAME, "Stu"); 3475 nameValues.put(StructuredName.FAMILY_NAME, "Goulash"); 3476 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo"); 3477 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH"); 3478 Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, nameValues); 3479 3480 long contactId = queryContactId(rawContactId); 3481 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 3482 3483 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash"); 3484 values.put(Contacts.TIMES_CONTACTED, 0); 3485 assertStoredValuesWithProjection(filterUri1, values); 3486 3487 assertContactFilter(contactId, "goolash"); 3488 assertContactFilter(contactId, "lash"); 3489 3490 assertContactFilterNoResult("goolish"); 3491 3492 // Phonetic name with given/family reversed should not match 3493 assertContactFilterNoResult("lashgoo"); 3494 3495 nameValues.clear(); 3496 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga"); 3497 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh"); 3498 3499 mResolver.update(nameUri, nameValues, null, null); 3500 3501 assertContactFilter(contactId, "galosh"); 3502 3503 assertContactFilterNoResult("goolish"); 3504 } 3505 3506 @Test testQueryContactFilterByEmailAddress()3507 public void testQueryContactFilterByEmailAddress() { 3508 ContentValues values = new ContentValues(); 3509 long rawContactId = createRawContact(values, "18004664411", 3510 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3511 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 3512 StatusUpdates.CAPABILITY_HAS_VOICE); 3513 3514 DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond"); 3515 3516 long contactId = queryContactId(rawContactId); 3517 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 3518 3519 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "[email protected]"); 3520 values.put(Contacts.TIMES_CONTACTED, 0); 3521 assertStoredValuesWithProjection(filterUri1, values); 3522 3523 assertContactFilter(contactId, "goog"); 3524 assertContactFilter(contactId, "goog411"); 3525 assertContactFilter(contactId, "goog411@"); 3526 assertContactFilter(contactId, "goog411@acme"); 3527 assertContactFilter(contactId, "[email protected]"); 3528 3529 assertContactFilterNoResult("[email protected]"); 3530 assertContactFilterNoResult("[email protected]"); 3531 assertContactFilterNoResult("goolish"); 3532 } 3533 3534 @Test testQueryContactFilterByPhoneNumber()3535 public void testQueryContactFilterByPhoneNumber() { 3536 ContentValues values = new ContentValues(); 3537 long rawContactId = createRawContact(values, "18004664411", 3538 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 3539 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 3540 StatusUpdates.CAPABILITY_HAS_VOICE); 3541 3542 DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond"); 3543 3544 long contactId = queryContactId(rawContactId); 3545 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 3546 3547 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411"); 3548 values.put(Contacts.TIMES_CONTACTED, 0); 3549 assertStoredValuesWithProjection(filterUri1, values); 3550 3551 assertContactFilter(contactId, "18004664411"); 3552 assertContactFilter(contactId, "1800466"); 3553 assertContactFilter(contactId, "+18004664411"); 3554 assertContactFilter(contactId, "8004664411"); 3555 3556 assertContactFilterNoResult("78004664411"); 3557 assertContactFilterNoResult("18004664412"); 3558 assertContactFilterNoResult("8884664411"); 3559 } 3560 3561 /** 3562 * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred 3563 * contacts. 3564 */ 3565 @Test testQueryContactStrequent()3566 public void testQueryContactStrequent() { 3567 ContentValues values1 = new ContentValues(); 3568 final String email1 = "[email protected]"; 3569 final String phoneNumber1 = "18004664411"; 3570 final int timesContacted1 = 0; 3571 createContact(values1, "Noah", "Tever", phoneNumber1, 3572 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0, 3573 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 3574 final String phoneNumber2 = "18004664412"; 3575 ContentValues values2 = new ContentValues(); 3576 createContact(values2, "Sam", "Times", phoneNumber2, 3577 "[email protected]", StatusUpdates.INVISIBLE, 3, 0, 0, 3578 StatusUpdates.CAPABILITY_HAS_CAMERA); 3579 ContentValues values3 = new ContentValues(); 3580 final String phoneNumber3 = "18004664413"; 3581 final int timesContacted3 = 9; 3582 createContact(values3, "Lotta", "Calling", phoneNumber3, 3583 "[email protected]", StatusUpdates.AWAY, timesContacted3, 0, 0, 3584 StatusUpdates.CAPABILITY_HAS_VIDEO); 3585 ContentValues values4 = new ContentValues(); 3586 final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null, 3587 "[email protected]", StatusUpdates.AVAILABLE, 0, 1, 0, 3588 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE); 3589 3590 // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data 3591 // usage feedback should be used for "frequently contacted" listing. 3592 assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4); 3593 3594 // Send feedback for the 3rd phone number, pretending we called that person via phone. 3595 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 3596 3597 values3.put(Contacts.TIMES_CONTACTED, 0); 3598 3599 // After the feedback, 3rd contact should be shown after starred one. 3600 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 3601 new ContentValues[] { values4 }); 3602 3603 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3604 // Twice. 3605 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3606 3607 // After the feedback, 1st and 3rd contacts should be shown after starred one. 3608 values1.put(Contacts.TIMES_CONTACTED, 0); 3609 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 3610 new ContentValues[] { values4 }); 3611 3612 // With phone-only parameter, 1st and 4th contacts shouldn't be returned because: 3613 // 1st: feedbacks are only about email, not about phone call. 3614 // 4th: it has no phone number though starred. 3615 Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon() 3616 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true") 3617 .build(); 3618 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { }); 3619 } 3620 3621 @Test testQueryContactStrequentFrequentOrder()3622 public void testQueryContactStrequentFrequentOrder() { 3623 // Prepare test data 3624 final long rid1 = RawContactUtil.createRawContact(mResolver); 3625 final long did1 = ContentUris.parseId(insertPhoneNumber(rid1, "1")); 3626 final long did1e = ContentUris.parseId(insertEmail(rid1, "[email protected]")); 3627 3628 final long rid2 = RawContactUtil.createRawContact(mResolver); 3629 final long did2 = ContentUris.parseId(insertPhoneNumber(rid2, "2")); 3630 3631 final long rid3 = RawContactUtil.createRawContact(mResolver); 3632 final long did3 = ContentUris.parseId(insertPhoneNumber(rid3, "3")); 3633 3634 final long rid4 = RawContactUtil.createRawContact(mResolver); 3635 final long did4 = ContentUris.parseId(insertPhoneNumber(rid4, "4")); 3636 3637 final long rid5 = RawContactUtil.createRawContact(mResolver); 3638 final long did5 = ContentUris.parseId(insertPhoneNumber(rid5, "5")); 3639 3640 final long rid6 = RawContactUtil.createRawContact(mResolver); 3641 final long did6 = ContentUris.parseId(insertPhoneNumber(rid6, "6")); 3642 3643 final long rid7 = RawContactUtil.createRawContact(mResolver); 3644 final long did7 = ContentUris.parseId(insertPhoneNumber(rid7, "7")); 3645 3646 final long rid8 = RawContactUtil.createRawContact(mResolver); 3647 final long did8 = ContentUris.parseId(insertPhoneNumber(rid8, "8")); 3648 3649 final long cid1 = queryContactId(rid1); 3650 final long cid2 = queryContactId(rid2); 3651 final long cid3 = queryContactId(rid3); 3652 final long cid4 = queryContactId(rid4); 3653 final long cid5 = queryContactId(rid5); 3654 final long cid6 = queryContactId(rid6); 3655 final long cid7 = queryContactId(rid7); 3656 final long cid8 = queryContactId(rid8); 3657 3658 // Make sure they aren't aggregated. 3659 EvenMoreAsserts.assertUnique(cid1, cid2, cid3, cid4, cid5, cid6, cid7, cid8); 3660 3661 // Prepare the clock 3662 sMockClock.install(); 3663 3664 // We check the timestamp in SQL, which doesn't know about the MockClock. So we need to 3665 // use the actual (roughly) time. 3666 3667 final long nowInMillis = System.currentTimeMillis(); 3668 final long oneDayAgoInMillis = (nowInMillis - 24L * 60 * 60 * 1000); 3669 final long fourDaysAgoInMillis = (nowInMillis - 4L * 24 * 60 * 60 * 1000); 3670 final long eightDaysAgoInMillis = (nowInMillis - 8L * 24 * 60 * 60 * 1000); 3671 final long fifteenDaysAgoInMillis = (nowInMillis - 15L * 24 * 60 * 60 * 1000); 3672 // All contacts older than 30 days will not be included in frequents 3673 final long thirtyOneDaysAgoInMillis = (nowInMillis - 31L * 24 * 60 * 60 * 1000); 3674 3675 // Contacts in this bucket are considered more than 30 days old 3676 sMockClock.setCurrentTimeMillis(thirtyOneDaysAgoInMillis); 3677 3678 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1, did2); 3679 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1); 3680 3681 // Contacts in this bucket are considered more than 14 days old 3682 sMockClock.setCurrentTimeMillis(fifteenDaysAgoInMillis); 3683 3684 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3, did4); 3685 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3); 3686 3687 // Contacts in this bucket are considered more than 7 days old 3688 sMockClock.setCurrentTimeMillis(eightDaysAgoInMillis); 3689 3690 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5, did6); 3691 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5); 3692 3693 // Contact cid1 again, but it's an email, not a phone call. 3694 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e); 3695 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e); 3696 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e); 3697 3698 // Contacts in this bucket are considered more than 3 days old 3699 sMockClock.setCurrentTimeMillis(fourDaysAgoInMillis); 3700 3701 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7); 3702 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7); 3703 3704 3705 // Contacts in this bucket are considered less than 3 days old 3706 sMockClock.setCurrentTimeMillis(oneDayAgoInMillis); 3707 3708 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did8); 3709 3710 sMockClock.setCurrentTimeMillis(nowInMillis); 3711 3712 // Check the order -- The regular frequent, which is contact based. 3713 // Note because we contacted cid1 8 days ago, it's been contacted 3 times, so it comes 3714 // before cid5 and cid6, which were contacted at the same time. 3715 // cid2 will not show up because it was contacted more than 30 days ago 3716 3717 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI); 3718 3719 // Check the order -- phone only frequent, which is data based. 3720 // Note this is based on data, and only looks at phone numbers, so the order is different 3721 // now. 3722 // did1, did2 will not show up because they were used to make calls more than 30 days ago. 3723 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI.buildUpon() 3724 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "1").build()); 3725 } 3726 3727 /** 3728 * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently 3729 * contacted person ordered by number of times contacted. 3730 */ 3731 @Test testQueryContactFrequent()3732 public void testQueryContactFrequent() { 3733 ContentValues values1 = new ContentValues(); 3734 final String email1 = "[email protected]"; 3735 createContact(values1, "Noah", "Tever", "18004664411", 3736 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3737 ContentValues values2 = new ContentValues(); 3738 final String email2 = "[email protected]"; 3739 createContact(values2, "Sam", "Times", "18004664412", 3740 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0); 3741 ContentValues values3 = new ContentValues(); 3742 final String phoneNumber3 = "18004664413"; 3743 final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3, 3744 "[email protected]", StatusUpdates.AWAY, 0, 1, 0, 0); 3745 ContentValues values4 = new ContentValues(); 3746 createContact(values4, "Fay", "Veritt", "18004664414", 3747 "[email protected]", StatusUpdates.AVAILABLE, 0, 1, 0, 0); 3748 3749 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3750 3751 assertStoredValues(Contacts.CONTENT_FREQUENT_URI); 3752 3753 // Pretend email was sent to the address twice. 3754 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3755 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3756 3757 values1.put(Contacts.TIMES_CONTACTED, 0); 3758 values2.put(Contacts.TIMES_CONTACTED, 0); 3759 assertStoredValues(Contacts.CONTENT_FREQUENT_URI); 3760 3761 for (int i = 0; i < 10; i++) { 3762 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 3763 } 3764 3765 values3.put(Contacts.TIMES_CONTACTED, 0); 3766 3767 assertStoredValues(Contacts.CONTENT_FREQUENT_URI); 3768 3769 3770 // Test it works with selection/selectionArgs 3771 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3772 Contacts.STARRED + "=?", new String[] {"0"} 3773 ); 3774 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3775 Contacts.STARRED + "=?", new String[] {"1"} 3776 ); 3777 3778 values3.put(Contacts.STARRED, 0); 3779 assertEquals(1, 3780 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI, 3781 String.valueOf(contactId3)), 3782 values3, null, null)); 3783 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3784 Contacts.STARRED + "=?", new String[] {"0"} 3785 ); 3786 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3787 Contacts.STARRED + "=?", new String[] {"1"} 3788 ); 3789 } 3790 3791 @Test testQueryContactFrequentExcludingInvisible()3792 public void testQueryContactFrequentExcludingInvisible() { 3793 ContentValues values1 = new ContentValues(); 3794 final String email1 = "[email protected]"; 3795 final long cid1 = createContact(values1, "Noah", "Tever", "18004664411", 3796 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3797 ContentValues values2 = new ContentValues(); 3798 final String email2 = "[email protected]"; 3799 final long cid2 = createContact(values2, "Sam", "Times", "18004664412", 3800 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0); 3801 3802 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3803 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3804 3805 // First, we have two contacts in frequent. 3806 assertStoredValues(Contacts.CONTENT_FREQUENT_URI); 3807 3808 // Contact 2 goes invisible. 3809 markInvisible(cid2); 3810 3811 // Now we have only 1 frequent. 3812 assertStoredValues(Contacts.CONTENT_FREQUENT_URI); 3813 3814 } 3815 3816 @Test testQueryDataUsageStat()3817 public void testQueryDataUsageStat() { 3818 // Now all data usage stats are zero as of Q. 3819 3820 ContentValues values1 = new ContentValues(); 3821 final String email1 = "[email protected]"; 3822 final long cid1 = createContact(values1, "Noah", "Tever", "18004664411", 3823 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3824 3825 sMockClock.install(); 3826 sMockClock.setCurrentTimeMillis(100); 3827 3828 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3829 3830 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3831 3832 sMockClock.setCurrentTimeMillis(86400 + 123); 3833 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3834 3835 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3836 3837 sMockClock.setCurrentTimeMillis(86400 * 3 + 123); 3838 for (int i = 0; i < 11; i++) { 3839 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1); 3840 } 3841 3842 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3843 3844 final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter( 3845 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build(); 3846 3847 assertDataUsageZero(dataUriWithUsageTypeLongText, "[email protected]"); 3848 3849 sMockClock.setCurrentTimeMillis(86400 * 4 + 123); 3850 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3851 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3852 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3853 3854 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3855 3856 sMockClock.setCurrentTimeMillis(86400 * 5 + 123); 3857 for (int i = 0; i < 10; i++) { 3858 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3859 } 3860 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3861 3862 sMockClock.setCurrentTimeMillis(86400 * 6 + 123); 3863 for (int i = 0; i < 10; i++) { 3864 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3865 } 3866 assertDataUsageZero(Data.CONTENT_URI, "[email protected]"); 3867 3868 final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter( 3869 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build(); 3870 3871 assertDataUsageZero(dataUriWithUsageTypeCall, "[email protected]"); 3872 } 3873 3874 @Test testQueryContactGroup()3875 public void testQueryContactGroup() { 3876 long groupId = createGroup(null, "testGroup", "Test Group"); 3877 3878 ContentValues values1 = new ContentValues(); 3879 createContact(values1, "Best", "West", "18004664411", 3880 "[email protected]", StatusUpdates.OFFLINE, 0, 0, groupId, 3881 StatusUpdates.CAPABILITY_HAS_CAMERA); 3882 3883 ContentValues values2 = new ContentValues(); 3884 createContact(values2, "Rest", "East", "18004664422", 3885 "[email protected]", StatusUpdates.AVAILABLE, 0, 0, 0, 3886 StatusUpdates.CAPABILITY_HAS_VOICE); 3887 3888 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 3889 Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID); 3890 assertEquals(1, c.getCount()); 3891 c.moveToFirst(); 3892 dumpCursor(c); 3893 assertCursorValues(c, values1); 3894 c.close(); 3895 3896 Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 3897 c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?", 3898 new String[] { "Best West" }, Contacts._ID); 3899 assertEquals(1, c.getCount()); 3900 c.close(); 3901 3902 Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group"); 3903 c = mResolver.query(filterUri3, null, null, null, Contacts._ID); 3904 assertEquals(0, c.getCount()); 3905 c.close(); 3906 } 3907 expectNoSecurityException(String failureMessage, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)3908 private void expectNoSecurityException(String failureMessage, Uri uri, String[] projection, 3909 String selection, String[] selectionArgs, String sortOrder) { 3910 Cursor c = null; 3911 try { 3912 c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder); 3913 } catch (SecurityException expected) { 3914 fail(failureMessage); 3915 } finally { 3916 if (c != null) { 3917 c.close(); 3918 } 3919 } 3920 } 3921 expectSecurityException(String failureMessage, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)3922 private void expectSecurityException(String failureMessage, Uri uri, String[] projection, 3923 String selection, String[] selectionArgs, String sortOrder) { 3924 Cursor c = null; 3925 try { 3926 c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder); 3927 fail(failureMessage); 3928 } catch (SecurityException expected) { 3929 // The security exception is expected to occur because we're missing a permission. 3930 } finally { 3931 if (c != null) { 3932 c.close(); 3933 } 3934 } 3935 } 3936 3937 @Test testQueryProfileWithoutPermission()3938 public void testQueryProfileWithoutPermission() { 3939 createBasicProfileContact(new ContentValues()); 3940 3941 // Case 1: Retrieving profile contact. 3942 expectNoSecurityException( 3943 "Querying for the profile without READ_PROFILE access should succeed.", 3944 Profile.CONTENT_URI, null, null, null, Contacts._ID); 3945 3946 // Case 2: Retrieving profile data. 3947 expectNoSecurityException( 3948 "Querying for the profile data without READ_PROFILE access should succeed.", 3949 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3950 null, null, null, Contacts._ID); 3951 3952 // Case 3: Retrieving profile entities. 3953 expectNoSecurityException( 3954 "Querying for the profile entities without READ_PROFILE access should succeed.", 3955 Profile.CONTENT_URI.buildUpon() 3956 .appendPath("entities").build(), null, null, null, Contacts._ID); 3957 } 3958 3959 @Test testQueryProfileByContactIdWithoutReadPermission()3960 public void testQueryProfileByContactIdWithoutReadPermission() { 3961 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3962 long profileContactId = queryContactId(profileRawContactId); 3963 3964 // A query for the profile contact by ID should not require READ_PROFILE. 3965 expectNoSecurityException( 3966 "Querying for the profile by contact ID without READ_PROFILE access should succeed", 3967 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId), 3968 null, null, null, Contacts._ID); 3969 } 3970 3971 @Test testQueryProfileByRawContactIdWithoutReadPermission()3972 public void testQueryProfileByRawContactIdWithoutReadPermission() { 3973 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3974 3975 expectNoSecurityException( 3976 "Querying for the raw contact profile without READ_PROFILE access should succeed.", 3977 ContentUris.withAppendedId(RawContacts.CONTENT_URI, 3978 profileRawContactId), null, null, null, RawContacts._ID); 3979 } 3980 3981 @Test testQueryProfileRawContactWithoutReadPermission()3982 public void testQueryProfileRawContactWithoutReadPermission() { 3983 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3984 3985 // Case 1: Retrieve the overall raw contact set for the profile. 3986 expectNoSecurityException( 3987 "Querying for the raw contact profile without READ_PROFILE access should succeed.", 3988 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null); 3989 3990 // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID. 3991 expectNoSecurityException( 3992 "Querying for the raw profile data without READ_PROFILE access should succeed.", 3993 ContentUris.withAppendedId( 3994 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 3995 .appendPath("data").build(), null, null, null, null); 3996 3997 // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID. 3998 expectNoSecurityException( 3999 "Querying for the raw profile entities without READ_PROFILE access should succeed.", 4000 ContentUris.withAppendedId( 4001 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 4002 .appendPath("entity").build(), null, null, null, null); 4003 } 4004 4005 @Test testQueryProfileDataByDataIdWithoutReadPermission()4006 public void testQueryProfileDataByDataIdWithoutReadPermission() { 4007 createBasicProfileContact(new ContentValues()); 4008 Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 4009 new String[]{Data._ID, Data.MIMETYPE}, null, null, null); 4010 assertEquals(4, c.getCount()); // Photo, phone, email, name. 4011 c.moveToFirst(); 4012 long profileDataId = c.getLong(0); 4013 c.close(); 4014 4015 expectNoSecurityException( 4016 "Querying for the data in the profile without READ_PROFILE access should succeed.", 4017 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId), 4018 null, null, null, null); 4019 } 4020 4021 @Test testQueryProfileDataWithoutReadPermission()4022 public void testQueryProfileDataWithoutReadPermission() { 4023 createBasicProfileContact(new ContentValues()); 4024 4025 expectNoSecurityException( 4026 "Querying for the data in the profile without READ_PROFILE access should succeed.", 4027 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 4028 null, null, null, null); 4029 } 4030 4031 @Test testInsertProfileWithoutWritePermission()4032 public void testInsertProfileWithoutWritePermission() { 4033 // Creating a non-profile contact should be fine. 4034 createBasicNonProfileContact(new ContentValues()); 4035 4036 try { 4037 createBasicProfileContact(new ContentValues()); 4038 } catch (SecurityException expected) { 4039 fail("Creating a profile contact should not require WRITE_PROFILE access."); 4040 } 4041 } 4042 4043 @Test testInsertProfileDataWithoutWritePermission()4044 public void testInsertProfileDataWithoutWritePermission() { 4045 long profileRawContactId = createBasicProfileContact(new ContentValues()); 4046 4047 try { 4048 insertEmail(profileRawContactId, "[email protected]", false); 4049 } catch (SecurityException expected) { 4050 fail("Inserting data into a profile contact should not require WRITE_PROFILE access."); 4051 } 4052 } 4053 4054 @Test testUpdateDataDoesNotRequireProfilePermission()4055 public void testUpdateDataDoesNotRequireProfilePermission() { 4056 // Create a non-profile contact. 4057 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Domo", "Arigato"); 4058 long dataId = getStoredLongValue(Data.CONTENT_URI, 4059 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", 4060 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE}, 4061 Data._ID); 4062 4063 // Updates its name using a selection. 4064 ContentValues values = new ContentValues(); 4065 values.put(StructuredName.GIVEN_NAME, "Bob"); 4066 values.put(StructuredName.FAMILY_NAME, "Blob"); 4067 mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?", 4068 new String[]{String.valueOf(dataId)}); 4069 4070 // Check that the update went through. 4071 assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values); 4072 } 4073 4074 @Test testQueryContactThenProfile()4075 public void testQueryContactThenProfile() { 4076 ContentValues profileValues = new ContentValues(); 4077 long profileRawContactId = createBasicProfileContact(profileValues); 4078 long profileContactId = queryContactId(profileRawContactId); 4079 4080 ContentValues nonProfileValues = new ContentValues(); 4081 long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues); 4082 long nonProfileContactId = queryContactId(nonProfileRawContactId); 4083 4084 nonProfileValues.put(Contacts.TIMES_CONTACTED, 0); 4085 profileValues.put(Contacts.TIMES_CONTACTED, 0); 4086 4087 assertStoredValues(Contacts.CONTENT_URI, nonProfileValues); 4088 4089 assertStoredValues(Profile.CONTENT_URI, profileValues); 4090 } 4091 4092 @Test testQueryContactExcludeProfile()4093 public void testQueryContactExcludeProfile() { 4094 // Create a profile contact (it should not be returned by the general contact URI). 4095 createBasicProfileContact(new ContentValues()); 4096 4097 // Create a non-profile contact - this should be returned. 4098 ContentValues nonProfileValues = new ContentValues(); 4099 createBasicNonProfileContact(nonProfileValues); 4100 nonProfileValues.put(Contacts.TIMES_CONTACTED, 0); 4101 assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues}); 4102 } 4103 4104 @Test testQueryProfile()4105 public void testQueryProfile() { 4106 ContentValues profileValues = new ContentValues(); 4107 createBasicProfileContact(profileValues); 4108 4109 profileValues.put(Contacts.TIMES_CONTACTED, 0); 4110 assertStoredValues(Profile.CONTENT_URI, profileValues); 4111 } 4112 getExpectedProfileDataValues()4113 private ContentValues[] getExpectedProfileDataValues() { 4114 // Expected photo data values (only field is the photo BLOB, which we can't check). 4115 ContentValues photoRow = new ContentValues(); 4116 photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 4117 4118 // Expected phone data values. 4119 ContentValues phoneRow = new ContentValues(); 4120 phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 4121 phoneRow.put(Phone.NUMBER, "18005554411"); 4122 4123 // Expected email data values. 4124 ContentValues emailRow = new ContentValues(); 4125 emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 4126 emailRow.put(Email.ADDRESS, "[email protected]"); 4127 4128 // Expected name data values. 4129 ContentValues nameRow = new ContentValues(); 4130 nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 4131 nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl"); 4132 nameRow.put(StructuredName.GIVEN_NAME, "Mia"); 4133 nameRow.put(StructuredName.FAMILY_NAME, "Prophyl"); 4134 4135 return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow}; 4136 } 4137 4138 @Test testQueryProfileData()4139 public void testQueryProfileData() { 4140 createBasicProfileContact(new ContentValues()); 4141 4142 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 4143 getExpectedProfileDataValues()); 4144 } 4145 4146 @Test testQueryProfileEntities()4147 public void testQueryProfileEntities() { 4148 createBasicProfileContact(new ContentValues()); 4149 4150 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(), 4151 getExpectedProfileDataValues()); 4152 } 4153 4154 @Test testQueryRawProfile()4155 public void testQueryRawProfile() { 4156 ContentValues profileValues = new ContentValues(); 4157 createBasicProfileContact(profileValues); 4158 4159 // The raw contact view doesn't include the photo ID. 4160 profileValues.remove(Contacts.PHOTO_ID); 4161 profileValues.put(Contacts.TIMES_CONTACTED, 0); 4162 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues); 4163 } 4164 4165 @Test testQueryRawProfileById()4166 public void testQueryRawProfileById() { 4167 ContentValues profileValues = new ContentValues(); 4168 long profileRawContactId = createBasicProfileContact(profileValues); 4169 4170 // The raw contact view doesn't include the photo ID. 4171 profileValues.remove(Contacts.PHOTO_ID); 4172 profileValues.put(Contacts.TIMES_CONTACTED, 0); 4173 assertStoredValues(ContentUris.withAppendedId( 4174 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues); 4175 } 4176 4177 @Test testQueryRawProfileData()4178 public void testQueryRawProfileData() { 4179 long profileRawContactId = createBasicProfileContact(new ContentValues()); 4180 4181 assertStoredValues(ContentUris.withAppendedId( 4182 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 4183 .appendPath("data").build(), getExpectedProfileDataValues()); 4184 } 4185 4186 @Test testQueryRawProfileEntity()4187 public void testQueryRawProfileEntity() { 4188 long profileRawContactId = createBasicProfileContact(new ContentValues()); 4189 4190 assertStoredValues(ContentUris.withAppendedId( 4191 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 4192 .appendPath("entity").build(), getExpectedProfileDataValues()); 4193 } 4194 4195 @Test testQueryDataForProfile()4196 public void testQueryDataForProfile() { 4197 createBasicProfileContact(new ContentValues()); 4198 4199 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 4200 getExpectedProfileDataValues()); 4201 } 4202 4203 @Test testUpdateProfileRawContact()4204 public void testUpdateProfileRawContact() { 4205 createBasicProfileContact(new ContentValues()); 4206 ContentValues updatedValues = new ContentValues(); 4207 updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0); 4208 updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3"); 4209 updatedValues.put(RawContacts.STARRED, 1); 4210 mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null); 4211 4212 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues); 4213 } 4214 4215 @Test testInsertProfileWithDataSetTriggersAccountCreation()4216 public void testInsertProfileWithDataSetTriggersAccountCreation() { 4217 // Check that we have no profile raw contacts. 4218 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{}); 4219 4220 // Insert a profile record with a new data set. 4221 Account account = new Account("a", "b"); 4222 String dataSet = "c"; 4223 Uri profileUri = TestUtil.maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI, 4224 account) 4225 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build(); 4226 ContentValues values = new ContentValues(); 4227 long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values)); 4228 values.put(RawContacts._ID, rawContactId); 4229 4230 // Check that querying for the profile gets the created raw contact. 4231 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values); 4232 } 4233 4234 @Test testLoadProfilePhoto()4235 public void testLoadProfilePhoto() throws IOException { 4236 long rawContactId = createBasicProfileContact(new ContentValues()); 4237 insertPhoto(rawContactId, R.drawable.earth_normal); 4238 EvenMoreAsserts.assertImageRawData(getContext(), 4239 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL), 4240 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false)); 4241 } 4242 4243 @Test testLoadProfileDisplayPhoto()4244 public void testLoadProfileDisplayPhoto() throws IOException { 4245 long rawContactId = createBasicProfileContact(new ContentValues()); 4246 insertPhoto(rawContactId, R.drawable.earth_normal); 4247 EvenMoreAsserts.assertImageRawData(getContext(), 4248 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4249 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, true)); 4250 } 4251 4252 @Test testPhonesWithStatusUpdate()4253 public void testPhonesWithStatusUpdate() { 4254 4255 ContentValues values = new ContentValues(); 4256 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4257 long rawContactId = ContentUris.parseId(rawContactUri); 4258 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 4259 Uri photoUri = insertPhoto(rawContactId); 4260 long photoId = ContentUris.parseId(photoUri); 4261 insertPhoneNumber(rawContactId, "18004664411"); 4262 insertPhoneNumber(rawContactId, "18004664412"); 4263 insertEmail(rawContactId, "[email protected]"); 4264 insertEmail(rawContactId, "[email protected]"); 4265 4266 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 4267 StatusUpdates.INVISIBLE, "Bad", 4268 StatusUpdates.CAPABILITY_HAS_CAMERA); 4269 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 4270 StatusUpdates.AVAILABLE, "Good", 4271 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE); 4272 long contactId = queryContactId(rawContactId); 4273 4274 Uri uri = Data.CONTENT_URI; 4275 4276 Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND " 4277 + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER); 4278 assertEquals(2, c.getCount()); 4279 4280 c.moveToFirst(); 4281 4282 values.clear(); 4283 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 4284 values.put(Contacts.CONTACT_STATUS, "Bad"); 4285 values.put(Contacts.DISPLAY_NAME, "John Doe"); 4286 values.put(Phone.NUMBER, "18004664411"); 4287 values.putNull(Phone.LABEL); 4288 values.put(RawContacts.CONTACT_ID, contactId); 4289 assertCursorValues(c, values); 4290 4291 c.moveToNext(); 4292 4293 values.clear(); 4294 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 4295 values.put(Contacts.CONTACT_STATUS, "Bad"); 4296 values.put(Contacts.DISPLAY_NAME, "John Doe"); 4297 values.put(Phone.NUMBER, "18004664412"); 4298 values.putNull(Phone.LABEL); 4299 values.put(RawContacts.CONTACT_ID, contactId); 4300 assertCursorValues(c, values); 4301 4302 c.close(); 4303 } 4304 4305 @Test testGroupQuery()4306 public void testGroupQuery() { 4307 Account account1 = new Account("a", "b"); 4308 Account account2 = new Account("c", "d"); 4309 long groupId1 = createGroup(account1, "e", "f"); 4310 long groupId2 = createGroup(account2, "g", "h"); 4311 Uri uri1 = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1); 4312 Uri uri2 = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2); 4313 assertEquals(1, getCount(uri1, null, null)); 4314 assertEquals(1, getCount(uri2, null, null)); 4315 assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ; 4316 assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ; 4317 } 4318 4319 @Test testGroupInsert()4320 public void testGroupInsert() { 4321 ContentValues values = new ContentValues(); 4322 4323 values.put(Groups.ACCOUNT_NAME, "a"); 4324 values.put(Groups.ACCOUNT_TYPE, "b"); 4325 values.put(Groups.DATA_SET, "ds"); 4326 values.put(Groups.SOURCE_ID, "c"); 4327 values.put(Groups.VERSION, 42); 4328 values.put(Groups.GROUP_VISIBLE, 1); 4329 values.put(Groups.TITLE, "d"); 4330 values.put(Groups.TITLE_RES, 1234); 4331 values.put(Groups.NOTES, "e"); 4332 values.put(Groups.RES_PACKAGE, "f"); 4333 values.put(Groups.SYSTEM_ID, "g"); 4334 values.put(Groups.DELETED, 1); 4335 values.put(Groups.SYNC1, "h"); 4336 values.put(Groups.SYNC2, "i"); 4337 values.put(Groups.SYNC3, "j"); 4338 values.put(Groups.SYNC4, "k"); 4339 4340 Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values); 4341 4342 values.put(Groups.DIRTY, 1); 4343 assertStoredValues(rowUri, values); 4344 } 4345 4346 @Test testGroupCreationAfterMembershipInsert()4347 public void testGroupCreationAfterMembershipInsert() { 4348 long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount); 4349 Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1"); 4350 4351 long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null); 4352 assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri), 4353 rawContactId1, groupId, "gsid1"); 4354 } 4355 4356 @Test testGroupReuseAfterMembershipInsert()4357 public void testGroupReuseAfterMembershipInsert() { 4358 long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount); 4359 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 4360 Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1"); 4361 4362 assertSingleGroup(groupId1, mAccount, "gsid1", "title1"); 4363 assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri), 4364 rawContactId1, groupId1, "gsid1"); 4365 } 4366 4367 @Test testGroupInsertFailureOnGroupIdConflict()4368 public void testGroupInsertFailureOnGroupIdConflict() { 4369 long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount); 4370 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 4371 4372 ContentValues values = new ContentValues(); 4373 values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1); 4374 values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 4375 values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1"); 4376 values.put(GroupMembership.GROUP_ROW_ID, groupId1); 4377 try { 4378 mResolver.insert(Data.CONTENT_URI, values); 4379 fail("the insert was expected to fail, but it succeeded"); 4380 } catch (IllegalArgumentException e) { 4381 // this was expected 4382 } 4383 } 4384 4385 @Test testGroupDelete_byAccountSelection()4386 public void testGroupDelete_byAccountSelection() { 4387 final Account account1 = new Account("accountName1", "accountType1"); 4388 final Account account2 = new Account("accountName2", "accountType2"); 4389 4390 final long groupId1 = createGroup(account1, "sourceId1", "title1"); 4391 final long groupId2 = createGroup(account2, "sourceId2", "title2"); 4392 final long groupId3 = createGroup(account2, "sourceId3", "title3"); 4393 4394 final int numDeleted = mResolver.delete(Groups.CONTENT_URI, 4395 Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?", 4396 new String[]{account2.name, account2.type}); 4397 assertEquals(2, numDeleted); 4398 4399 ContentValues v1 = new ContentValues(); 4400 v1.put(Groups._ID, groupId1); 4401 v1.put(Groups.DELETED, 0); 4402 4403 ContentValues v2 = new ContentValues(); 4404 v2.put(Groups._ID, groupId2); 4405 v2.put(Groups.DELETED, 1); 4406 4407 ContentValues v3 = new ContentValues(); 4408 v3.put(Groups._ID, groupId3); 4409 v3.put(Groups.DELETED, 1); 4410 4411 assertStoredValues(Groups.CONTENT_URI, new ContentValues[] { v1, v2, v3 }); 4412 } 4413 4414 @Test testGroupDelete_byAccountParam()4415 public void testGroupDelete_byAccountParam() { 4416 final Account account1 = new Account("accountName1", "accountType1"); 4417 final Account account2 = new Account("accountName2", "accountType2"); 4418 4419 final long groupId1 = createGroup(account1, "sourceId1", "title1"); 4420 final long groupId2 = createGroup(account2, "sourceId2", "title2"); 4421 final long groupId3 = createGroup(account2, "sourceId3", "title3"); 4422 4423 final int numDeleted = mResolver.delete( 4424 Groups.CONTENT_URI.buildUpon() 4425 .appendQueryParameter(Groups.ACCOUNT_NAME, account2.name) 4426 .appendQueryParameter(Groups.ACCOUNT_TYPE, account2.type) 4427 .build(), 4428 null, null); 4429 assertEquals(2, numDeleted); 4430 4431 ContentValues v1 = new ContentValues(); 4432 v1.put(Groups._ID, groupId1); 4433 v1.put(Groups.DELETED, 0); 4434 4435 ContentValues v2 = new ContentValues(); 4436 v2.put(Groups._ID, groupId2); 4437 v2.put(Groups.DELETED, 1); 4438 4439 ContentValues v3 = new ContentValues(); 4440 v3.put(Groups._ID, groupId3); 4441 v3.put(Groups.DELETED, 1); 4442 4443 assertStoredValues(Groups.CONTENT_URI, new ContentValues[] { v1, v2, v3 }); 4444 } 4445 4446 @Test testGroupSummaryQuery()4447 public void testGroupSummaryQuery() { 4448 final Account account1 = new Account("accountName1", "accountType1"); 4449 final Account account2 = new Account("accountName2", "accountType2"); 4450 final long groupId1 = createGroup(account1, "sourceId1", "title1"); 4451 final long groupId2 = createGroup(account2, "sourceId2", "title2"); 4452 final long groupId3 = createGroup(account2, "sourceId3", "title3"); 4453 4454 // Prepare raw contact id not used at all, to test group summary uri won't be confused 4455 // with it. 4456 final long rawContactId0 = RawContactUtil.createRawContactWithName(mResolver, "firstName0", 4457 "lastName0"); 4458 4459 final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "firstName1", 4460 "lastName1"); 4461 insertEmail(rawContactId1, "[email protected]"); 4462 insertGroupMembership(rawContactId1, groupId1); 4463 4464 final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "firstName2", 4465 "lastName2"); 4466 insertEmail(rawContactId2, "[email protected]"); 4467 insertPhoneNumber(rawContactId2, "222-222-2222"); 4468 insertGroupMembership(rawContactId2, groupId1); 4469 4470 ContentValues v1 = new ContentValues(); 4471 v1.put(Groups._ID, groupId1); 4472 v1.put(Groups.TITLE, "title1"); 4473 v1.put(Groups.SOURCE_ID, "sourceId1"); 4474 v1.put(Groups.ACCOUNT_NAME, account1.name); 4475 v1.put(Groups.ACCOUNT_TYPE, account1.type); 4476 v1.put(Groups.SUMMARY_COUNT, 2); 4477 v1.put(Groups.SUMMARY_WITH_PHONES, 1); 4478 4479 ContentValues v2 = new ContentValues(); 4480 v2.put(Groups._ID, groupId2); 4481 v2.put(Groups.TITLE, "title2"); 4482 v2.put(Groups.SOURCE_ID, "sourceId2"); 4483 v2.put(Groups.ACCOUNT_NAME, account2.name); 4484 v2.put(Groups.ACCOUNT_TYPE, account2.type); 4485 v2.put(Groups.SUMMARY_COUNT, 0); 4486 v2.put(Groups.SUMMARY_WITH_PHONES, 0); 4487 4488 ContentValues v3 = new ContentValues(); 4489 v3.put(Groups._ID, groupId3); 4490 v3.put(Groups.TITLE, "title3"); 4491 v3.put(Groups.SOURCE_ID, "sourceId3"); 4492 v3.put(Groups.ACCOUNT_NAME, account2.name); 4493 v3.put(Groups.ACCOUNT_TYPE, account2.type); 4494 v3.put(Groups.SUMMARY_COUNT, 0); 4495 v3.put(Groups.SUMMARY_WITH_PHONES, 0); 4496 4497 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 4498 4499 // Now rawContactId1 has two phone numbers. 4500 insertPhoneNumber(rawContactId1, "111-111-1111"); 4501 insertPhoneNumber(rawContactId1, "111-111-1112"); 4502 // Result should reflect it correctly (don't count phone numbers but raw contacts) 4503 v1.put(Groups.SUMMARY_WITH_PHONES, v1.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1); 4504 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 4505 4506 // Introduce new raw contact, pretending the user added another info. 4507 final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "firstName3", 4508 "lastName3"); 4509 insertEmail(rawContactId3, "[email protected]"); 4510 insertPhoneNumber(rawContactId3, "333-333-3333"); 4511 insertGroupMembership(rawContactId3, groupId2); 4512 v2.put(Groups.SUMMARY_COUNT, v2.getAsInteger(Groups.SUMMARY_COUNT) + 1); 4513 v2.put(Groups.SUMMARY_WITH_PHONES, v2.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1); 4514 4515 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 4516 4517 final Uri uri = Groups.CONTENT_SUMMARY_URI; 4518 4519 // TODO Once SUMMARY_GROUP_COUNT_PER_ACCOUNT is supported remove all the if(false). 4520 if (false) { 4521 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1); 4522 v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2); 4523 v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2); 4524 } else { 4525 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0); 4526 v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0); 4527 v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0); 4528 } 4529 assertStoredValues(uri, new ContentValues[] { v1, v2, v3 }); 4530 4531 // Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly 4532 // reflects the change. 4533 final long groupId4 = createGroup(account1, "sourceId4", "title4"); 4534 if (false) { 4535 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 4536 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1); 4537 } else { 4538 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0); 4539 } 4540 ContentValues v4 = new ContentValues(); 4541 v4.put(Groups._ID, groupId4); 4542 v4.put(Groups.TITLE, "title4"); 4543 v4.put(Groups.SOURCE_ID, "sourceId4"); 4544 v4.put(Groups.ACCOUNT_NAME, account1.name); 4545 v4.put(Groups.ACCOUNT_TYPE, account1.type); 4546 v4.put(Groups.SUMMARY_COUNT, 0); 4547 v4.put(Groups.SUMMARY_WITH_PHONES, 0); 4548 if (false) { 4549 v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 4550 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT)); 4551 } else { 4552 v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0); 4553 } 4554 assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 }); 4555 4556 // We change the tables dynamically according to the requested projection. 4557 // Make sure the SUMMARY_COUNT column exists 4558 v1.clear(); 4559 v1.put(Groups.SUMMARY_COUNT, 2); 4560 v2.clear(); 4561 v2.put(Groups.SUMMARY_COUNT, 1); 4562 v3.clear(); 4563 v3.put(Groups.SUMMARY_COUNT, 0); 4564 v4.clear(); 4565 v4.put(Groups.SUMMARY_COUNT, 0); 4566 assertStoredValuesWithProjection(uri, new ContentValues[] { v1, v2, v3, v4 }); 4567 } 4568 4569 @Test testSettingsQuery()4570 public void testSettingsQuery() { 4571 Account account1 = new Account("a", "b"); 4572 Account account2 = new Account("c", "d"); 4573 AccountWithDataSet account3 = new AccountWithDataSet("e", "f", "plus"); 4574 createSettings(account1, "0", "0"); 4575 createSettings(account2, "1", "1"); 4576 createSettings(account3, "1", "0"); 4577 Uri uri1 = TestUtil.maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1); 4578 Uri uri2 = TestUtil.maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2); 4579 Uri uri3 = Settings.CONTENT_URI.buildUpon() 4580 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account3.getAccountName()) 4581 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account3.getAccountType()) 4582 .appendQueryParameter(RawContacts.DATA_SET, account3.getDataSet()) 4583 .build(); 4584 assertEquals(1, getCount(uri1, null, null)); 4585 assertEquals(1, getCount(uri2, null, null)); 4586 assertEquals(1, getCount(uri3, null, null)); 4587 assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ; 4588 assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0"); 4589 assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ; 4590 assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1"); 4591 assertStoredValue(uri3, Settings.SHOULD_SYNC, "1"); 4592 assertStoredValue(uri3, Settings.UNGROUPED_VISIBLE, "0"); 4593 } 4594 4595 @Test testSettingsInsertionPreventsDuplicates()4596 public void testSettingsInsertionPreventsDuplicates() { 4597 Account account1 = new Account("a", "b"); 4598 AccountWithDataSet account2 = new AccountWithDataSet("c", "d", "plus"); 4599 createSettings(account1, "0", "0"); 4600 createSettings(account2, "1", "1"); 4601 4602 // Now try creating the settings rows again. It should update the existing settings rows. 4603 createSettings(account1, "1", "0"); 4604 assertStoredValue(Settings.CONTENT_URI, 4605 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?", 4606 new String[] {"a", "b"}, Settings.SHOULD_SYNC, "1"); 4607 4608 createSettings(account2, "0", "1"); 4609 assertStoredValue(Settings.CONTENT_URI, 4610 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=? AND " + 4611 Settings.DATA_SET + "=?", 4612 new String[] {"c", "d", "plus"}, Settings.SHOULD_SYNC, "0"); 4613 } 4614 4615 @Test testSettingsDeletion()4616 public void testSettingsDeletion() { 4617 Account account = new Account("a", "b"); 4618 Uri settingUri = createSettings(account, "0", "1"); 4619 long rawContactId = RawContactUtil.createRawContact(mResolver, account); 4620 4621 int count = mResolver.delete(settingUri, null, null); 4622 4623 // Settings cannot be deleted when there are still raw contacts for the account. 4624 assertEquals(0, count); 4625 4626 assertStoredValue(Settings.CONTENT_URI, 4627 Settings.ACCOUNT_NAME + "= ? AND " + Settings.ACCOUNT_TYPE + "= ?", 4628 new String[] {"a", "b"}, Settings.UNGROUPED_VISIBLE, "1"); 4629 4630 RawContactUtil.delete(mResolver, rawContactId, true); 4631 4632 count = mResolver.delete(settingUri, null, null); 4633 4634 assertEquals(1, count); 4635 assertRowCount(0, Settings.CONTENT_URI, null, null); 4636 } 4637 4638 @Test testSettingsUpdate()4639 public void testSettingsUpdate() { 4640 Account account1 = new Account("a", "b"); 4641 Account account2 = new Account("c", "d"); 4642 Account account3 = new Account("e", "f"); 4643 createSettings(account1, "0", "0"); 4644 createSettings(account2, "0", "0"); 4645 createSettings(account3, "0", "0"); 4646 4647 ContentValues values = new ContentValues(); 4648 values.put(Settings.UNGROUPED_VISIBLE, 1); 4649 int count = mResolver.update(Settings.CONTENT_URI, values, null, null); 4650 4651 assertEquals(3, count); 4652 assertStoredValues(Settings.CONTENT_URI, 4653 cv(Settings.UNGROUPED_VISIBLE, 1), 4654 cv(Settings.UNGROUPED_VISIBLE, 1), 4655 cv(Settings.UNGROUPED_VISIBLE, 1)); 4656 4657 values.put(Settings.SHOULD_SYNC, 1); 4658 count = mResolver.update(Settings.CONTENT_URI, values, 4659 Settings.ACCOUNT_NAME + "=?", new String[] {"a"}); 4660 4661 assertEquals(1, count); 4662 assertStoredValues(Settings.CONTENT_URI, 4663 cv(Settings.ACCOUNT_NAME, "a", 4664 Settings.SHOULD_SYNC, 1), 4665 cv(Settings.ACCOUNT_NAME, "c", 4666 Settings.SHOULD_SYNC, 0), 4667 cv(Settings.ACCOUNT_NAME, "e", 4668 Settings.SHOULD_SYNC, 0)); 4669 4670 values.clear(); 4671 // Settings are stored in the accounts table but updates shouldn't be allowed to modify 4672 // the other non-Settings columns. 4673 values.put(Settings.ACCOUNT_NAME, "x"); 4674 values.put(Settings.ACCOUNT_TYPE, "y"); 4675 values.put(Settings.DATA_SET, "z"); 4676 mResolver.update(Settings.CONTENT_URI, values, null, null); 4677 4678 values.put(AccountsColumns.SIM_EF_TYPE, 1); 4679 values.put(AccountsColumns.SIM_SLOT_INDEX, 1); 4680 try { 4681 mResolver.update(Settings.CONTENT_URI, values, null, null); 4682 } catch (Exception e) { 4683 // ignored. We just care that the update didn't change the data 4684 } 4685 4686 assertStoredValuesDb("SELECT * FROM " + Tables.ACCOUNTS, null, 4687 cv( 4688 Settings.ACCOUNT_NAME, "a", 4689 Settings.ACCOUNT_TYPE, "b", 4690 Settings.DATA_SET, null, 4691 AccountsColumns.SIM_SLOT_INDEX, null, 4692 AccountsColumns.SIM_EF_TYPE, null 4693 ), 4694 cv( 4695 Settings.ACCOUNT_NAME, "c", 4696 Settings.ACCOUNT_TYPE, "d", 4697 Settings.DATA_SET, null, 4698 AccountsColumns.SIM_SLOT_INDEX, null, 4699 AccountsColumns.SIM_EF_TYPE, null 4700 ), 4701 cv( 4702 Settings.ACCOUNT_NAME, "e", 4703 Settings.ACCOUNT_TYPE, "f", 4704 Settings.DATA_SET, null, 4705 AccountsColumns.SIM_SLOT_INDEX, null, 4706 AccountsColumns.SIM_EF_TYPE, null 4707 )); 4708 } 4709 4710 @Test testSettingsLocalAccount()4711 public void testSettingsLocalAccount() { 4712 AccountWithDataSet localAccount = AccountWithDataSet.LOCAL; 4713 4714 // It's not possible to insert the local account directly into settings but it will be 4715 // created automatically when a raw contact is created for it. 4716 RawContactUtil.createRawContactWithAccountDataSet( 4717 mResolver, localAccount.getAccountName(), 4718 localAccount.getAccountType(), localAccount.getDataSet()); 4719 4720 ContentValues values = new ContentValues(); 4721 values.put(Settings.ACCOUNT_NAME, localAccount.getAccountName()); 4722 values.put(Settings.ACCOUNT_TYPE, localAccount.getAccountType()); 4723 values.put(Settings.DATA_SET, localAccount.getDataSet()); 4724 ContentValues expectedValues = new ContentValues(values); 4725 // The defaults for the local account are opposite of other accounts. 4726 expectedValues.put(Settings.UNGROUPED_VISIBLE, "1"); 4727 expectedValues.put(Settings.SHOULD_SYNC, "0"); 4728 4729 assertStoredValues(Settings.CONTENT_URI, expectedValues); 4730 4731 values.put(Settings.SHOULD_SYNC, 1); 4732 values.put(Settings.UNGROUPED_VISIBLE, 0); 4733 mResolver.update(Settings.CONTENT_URI, values, null, null); 4734 4735 expectedValues.put(Settings.UNGROUPED_VISIBLE, "0"); 4736 expectedValues.put(Settings.SHOULD_SYNC, "1"); 4737 assertStoredValues(Settings.CONTENT_URI, expectedValues); 4738 4739 // Empty strings should also be the local account. 4740 values.put(Settings.ACCOUNT_NAME, ""); 4741 values.put(Settings.ACCOUNT_TYPE, ""); 4742 values.put(Settings.DATA_SET, ""); 4743 mResolver.insert(Settings.CONTENT_URI, values); 4744 4745 assertRowCount(1, Settings.CONTENT_URI, null, null); 4746 } 4747 4748 @Test testDisplayNameParsingWhenPartsUnspecified()4749 public void testDisplayNameParsingWhenPartsUnspecified() { 4750 long rawContactId = RawContactUtil.createRawContact(mResolver); 4751 ContentValues values = new ContentValues(); 4752 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 4753 DataUtil.insertStructuredName(mResolver, rawContactId, values); 4754 4755 assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr."); 4756 } 4757 4758 @Test testDisplayNameParsingWhenPartsAreNull()4759 public void testDisplayNameParsingWhenPartsAreNull() { 4760 long rawContactId = RawContactUtil.createRawContact(mResolver); 4761 ContentValues values = new ContentValues(); 4762 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 4763 values.putNull(StructuredName.GIVEN_NAME); 4764 values.putNull(StructuredName.FAMILY_NAME); 4765 DataUtil.insertStructuredName(mResolver, rawContactId, values); 4766 assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr."); 4767 } 4768 4769 @Test testDisplayNameParsingWhenPartsSpecified()4770 public void testDisplayNameParsingWhenPartsSpecified() { 4771 long rawContactId = RawContactUtil.createRawContact(mResolver); 4772 ContentValues values = new ContentValues(); 4773 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 4774 values.put(StructuredName.FAMILY_NAME, "Johnson"); 4775 DataUtil.insertStructuredName(mResolver, rawContactId, values); 4776 4777 assertStructuredName(rawContactId, null, null, null, "Johnson", null); 4778 } 4779 4780 @Test testContactWithoutPhoneticName()4781 public void testContactWithoutPhoneticName() { 4782 ContactLocaleUtils.setLocaleForTest(Locale.ENGLISH); 4783 final long rawContactId = RawContactUtil.createRawContact(mResolver, null); 4784 4785 ContentValues values = new ContentValues(); 4786 values.put(StructuredName.PREFIX, "Mr"); 4787 values.put(StructuredName.GIVEN_NAME, "John"); 4788 values.put(StructuredName.MIDDLE_NAME, "K."); 4789 values.put(StructuredName.FAMILY_NAME, "Doe"); 4790 values.put(StructuredName.SUFFIX, "Jr."); 4791 Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values); 4792 4793 values.clear(); 4794 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4795 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr."); 4796 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr."); 4797 values.putNull(RawContacts.PHONETIC_NAME); 4798 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 4799 values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr."); 4800 values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "J"); 4801 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr."); 4802 values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D"); 4803 4804 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4805 assertStoredValues(rawContactUri, values); 4806 4807 values.clear(); 4808 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4809 values.put(Contacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr."); 4810 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr."); 4811 values.putNull(Contacts.PHONETIC_NAME); 4812 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 4813 values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr."); 4814 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "J"); 4815 values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr."); 4816 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D"); 4817 4818 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 4819 queryContactId(rawContactId)); 4820 assertStoredValues(contactUri, values); 4821 4822 // The same values should be available through a join with Data 4823 assertStoredValues(dataUri, values); 4824 } 4825 4826 @Test testContactWithChineseName()4827 public void testContactWithChineseName() { 4828 if (!hasChineseCollator()) { 4829 return; 4830 } 4831 ContactLocaleUtils.setLocaleForTest(Locale.SIMPLIFIED_CHINESE); 4832 4833 long rawContactId = RawContactUtil.createRawContact(mResolver, null); 4834 4835 ContentValues values = new ContentValues(); 4836 // "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B" 4837 values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B"); 4838 Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values); 4839 4840 values.clear(); 4841 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4842 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 4843 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 4844 values.putNull(RawContacts.PHONETIC_NAME); 4845 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 4846 values.put(RawContacts.SORT_KEY_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 4847 values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "D"); 4848 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 4849 values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D"); 4850 4851 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4852 assertStoredValues(rawContactUri, values); 4853 4854 values.clear(); 4855 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4856 values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 4857 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 4858 values.putNull(Contacts.PHONETIC_NAME); 4859 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 4860 values.put(Contacts.SORT_KEY_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 4861 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "D"); 4862 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 4863 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D"); 4864 4865 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 4866 queryContactId(rawContactId)); 4867 assertStoredValues(contactUri, values); 4868 4869 // The same values should be available through a join with Data 4870 assertStoredValues(dataUri, values); 4871 } 4872 4873 @Test testJapaneseNameContactInEnglishLocale()4874 public void testJapaneseNameContactInEnglishLocale() { 4875 // Need Japanese locale data for transliteration 4876 if (!hasJapaneseCollator()) { 4877 return; 4878 } 4879 ContactLocaleUtils.setLocaleForTest(Locale.US); 4880 long rawContactId = RawContactUtil.createRawContact(mResolver, null); 4881 4882 ContentValues values = new ContentValues(); 4883 values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77"); 4884 values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046"); 4885 DataUtil.insertStructuredName(mResolver, rawContactId, values); 4886 4887 long contactId = queryContactId(rawContactId); 4888 // en_US should behave same as ja_JP (match on Hiragana and Romaji 4889 // but not Pinyin) 4890 assertContactFilter(contactId, "\u304B\u3044\u304F\u3046"); 4891 assertContactFilter(contactId, "kaiku"); 4892 assertContactFilterNoResult("kong"); 4893 } 4894 4895 @Test testContactWithJapaneseName()4896 public void testContactWithJapaneseName() { 4897 if (!hasJapaneseCollator()) { 4898 return; 4899 } 4900 ContactLocaleUtils.setLocaleForTest(Locale.JAPAN); 4901 long rawContactId = RawContactUtil.createRawContact(mResolver, null); 4902 4903 ContentValues values = new ContentValues(); 4904 values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77"); 4905 values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046"); 4906 Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values); 4907 4908 values.clear(); 4909 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4910 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77"); 4911 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77"); 4912 values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046"); 4913 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 4914 values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046"); 4915 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046"); 4916 values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u304B"); 4917 values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u304B"); 4918 4919 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4920 assertStoredValues(rawContactUri, values); 4921 4922 values.clear(); 4923 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 4924 values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77"); 4925 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77"); 4926 values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046"); 4927 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 4928 values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046"); 4929 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046"); 4930 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u304B"); 4931 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u304B"); 4932 4933 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 4934 queryContactId(rawContactId)); 4935 assertStoredValues(contactUri, values); 4936 4937 // The same values should be available through a join with Data 4938 assertStoredValues(dataUri, values); 4939 4940 long contactId = queryContactId(rawContactId); 4941 // ja_JP should match on Hiragana and Romaji but not Pinyin 4942 assertContactFilter(contactId, "\u304B\u3044\u304F\u3046"); 4943 assertContactFilter(contactId, "kaiku"); 4944 assertContactFilterNoResult("kong"); 4945 } 4946 4947 @Test testDisplayNameUpdate()4948 public void testDisplayNameUpdate() { 4949 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 4950 insertEmail(rawContactId1, "[email protected]", true); 4951 4952 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 4953 insertPhoneNumber(rawContactId2, "123456789", true); 4954 4955 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 4956 rawContactId1, rawContactId2); 4957 4958 assertAggregated(rawContactId1, rawContactId2, "123456789"); 4959 4960 DataUtil.insertStructuredName(mResolver, rawContactId2, "Potato", "Head"); 4961 4962 assertAggregated(rawContactId1, rawContactId2, "Potato Head"); 4963 assertNetworkNotified(true); 4964 } 4965 4966 @Test testDisplayNameFromData()4967 public void testDisplayNameFromData() { 4968 long rawContactId = RawContactUtil.createRawContact(mResolver); 4969 long contactId = queryContactId(rawContactId); 4970 ContentValues values = new ContentValues(); 4971 4972 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4973 4974 assertStoredValue(uri, Contacts.DISPLAY_NAME, null); 4975 insertEmail(rawContactId, "[email protected]"); 4976 assertStoredValue(uri, Contacts.DISPLAY_NAME, "[email protected]"); 4977 4978 insertEmail(rawContactId, "[email protected]", true); 4979 assertStoredValue(uri, Contacts.DISPLAY_NAME, "[email protected]"); 4980 4981 insertPhoneNumber(rawContactId, "1-800-466-4411"); 4982 assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411"); 4983 4984 // If there are title and company, the company is display name. 4985 values.clear(); 4986 values.put(Organization.COMPANY, "Monsters Inc"); 4987 Uri organizationUri = insertOrganization(rawContactId, values); 4988 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc"); 4989 4990 // If there is nickname, that is display name. 4991 insertNickname(rawContactId, "Sully"); 4992 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully"); 4993 4994 // If there is structured name, that is display name. 4995 values.clear(); 4996 values.put(StructuredName.GIVEN_NAME, "James"); 4997 values.put(StructuredName.MIDDLE_NAME, "P."); 4998 values.put(StructuredName.FAMILY_NAME, "Sullivan"); 4999 DataUtil.insertStructuredName(mResolver, rawContactId, values); 5000 assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan"); 5001 } 5002 5003 @Test testDisplayNameFromOrganizationWithoutPhoneticName()5004 public void testDisplayNameFromOrganizationWithoutPhoneticName() { 5005 long rawContactId = RawContactUtil.createRawContact(mResolver); 5006 long contactId = queryContactId(rawContactId); 5007 ContentValues values = new ContentValues(); 5008 5009 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5010 5011 // If there is title without company, the title is display name. 5012 values.clear(); 5013 values.put(Organization.TITLE, "Protagonist"); 5014 Uri organizationUri = insertOrganization(rawContactId, values); 5015 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist"); 5016 5017 // If there are title and company, the company is display name. 5018 values.clear(); 5019 values.put(Organization.COMPANY, "Monsters Inc"); 5020 mResolver.update(organizationUri, values, null, null); 5021 5022 values.clear(); 5023 values.put(Contacts.DISPLAY_NAME, "Monsters Inc"); 5024 values.putNull(Contacts.PHONETIC_NAME); 5025 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 5026 values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc"); 5027 values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc"); 5028 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "M"); 5029 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "M"); 5030 assertStoredValues(uri, values); 5031 } 5032 5033 @Test testDisplayNameFromOrganizationWithJapanesePhoneticName()5034 public void testDisplayNameFromOrganizationWithJapanesePhoneticName() { 5035 if (!hasJapaneseCollator()) { 5036 return; 5037 } 5038 ContactLocaleUtils.setLocaleForTest(Locale.JAPAN); 5039 long rawContactId = RawContactUtil.createRawContact(mResolver); 5040 long contactId = queryContactId(rawContactId); 5041 ContentValues values = new ContentValues(); 5042 5043 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5044 5045 // If there is title without company, the title is display name. 5046 values.clear(); 5047 values.put(Organization.COMPANY, "DoCoMo"); 5048 values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2"); 5049 Uri organizationUri = insertOrganization(rawContactId, values); 5050 5051 values.clear(); 5052 values.put(Contacts.DISPLAY_NAME, "DoCoMo"); 5053 values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2"); 5054 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 5055 values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2"); 5056 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2"); 5057 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u305F"); 5058 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u305F"); 5059 assertStoredValues(uri, values); 5060 } 5061 5062 @Test testDisplayNameFromOrganizationWithChineseName()5063 public void testDisplayNameFromOrganizationWithChineseName() { 5064 if (!hasChineseCollator()) { 5065 return; 5066 } 5067 ContactLocaleUtils.setLocaleForTest(Locale.SIMPLIFIED_CHINESE); 5068 5069 long rawContactId = RawContactUtil.createRawContact(mResolver); 5070 long contactId = queryContactId(rawContactId); 5071 ContentValues values = new ContentValues(); 5072 5073 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5074 5075 // If there is title without company, the title is display name. 5076 values.clear(); 5077 values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1"); 5078 Uri organizationUri = insertOrganization(rawContactId, values); 5079 5080 values.clear(); 5081 values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1"); 5082 values.putNull(Contacts.PHONETIC_NAME); 5083 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 5084 values.put(Contacts.SORT_KEY_PRIMARY, "\u4E2D\u56FD\u7535\u4FE1"); 5085 values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "Z"); 5086 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u4E2D\u56FD\u7535\u4FE1"); 5087 values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "Z"); 5088 assertStoredValues(uri, values); 5089 } 5090 5091 @Test testLookupByOrganization()5092 public void testLookupByOrganization() { 5093 long rawContactId = RawContactUtil.createRawContact(mResolver); 5094 long contactId = queryContactId(rawContactId); 5095 ContentValues values = new ContentValues(); 5096 5097 values.clear(); 5098 values.put(Organization.COMPANY, "acmecorp"); 5099 values.put(Organization.TITLE, "president"); 5100 Uri organizationUri = insertOrganization(rawContactId, values); 5101 5102 assertContactFilter(contactId, "acmecorp"); 5103 assertContactFilter(contactId, "president"); 5104 5105 values.clear(); 5106 values.put(Organization.DEPARTMENT, "software"); 5107 mResolver.update(organizationUri, values, null, null); 5108 5109 assertContactFilter(contactId, "acmecorp"); 5110 assertContactFilter(contactId, "president"); 5111 5112 values.clear(); 5113 values.put(Organization.COMPANY, "incredibles"); 5114 mResolver.update(organizationUri, values, null, null); 5115 5116 assertContactFilter(contactId, "incredibles"); 5117 assertContactFilter(contactId, "president"); 5118 5119 values.clear(); 5120 values.put(Organization.TITLE, "director"); 5121 mResolver.update(organizationUri, values, null, null); 5122 5123 assertContactFilter(contactId, "incredibles"); 5124 assertContactFilter(contactId, "director"); 5125 5126 values.clear(); 5127 values.put(Organization.COMPANY, "monsters"); 5128 values.put(Organization.TITLE, "scarer"); 5129 mResolver.update(organizationUri, values, null, null); 5130 5131 assertContactFilter(contactId, "monsters"); 5132 assertContactFilter(contactId, "scarer"); 5133 } 5134 assertContactFilter(long contactId, String filter)5135 private void assertContactFilter(long contactId, String filter) { 5136 Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter)); 5137 assertStoredValue(filterUri, Contacts._ID, contactId); 5138 } 5139 assertContactFilterNoResult(String filter)5140 private void assertContactFilterNoResult(String filter) { 5141 Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter)); 5142 assertEquals(0, getCount(filterUri, null, null)); 5143 } 5144 5145 @Test testSearchSnippetOrganization()5146 public void testSearchSnippetOrganization() throws Exception { 5147 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 5148 long contactId = queryContactId(rawContactId); 5149 5150 // Some random data element 5151 insertEmail(rawContactId, "[email protected]"); 5152 5153 ContentValues values = new ContentValues(); 5154 values.clear(); 5155 values.put(Organization.COMPANY, "acmecorp"); 5156 values.put(Organization.TITLE, "engineer"); 5157 Uri organizationUri = insertOrganization(rawContactId, values); 5158 5159 // Add another matching organization 5160 values.put(Organization.COMPANY, "acmeinc"); 5161 insertOrganization(rawContactId, values); 5162 5163 // Add another non-matching organization 5164 values.put(Organization.COMPANY, "corpacme"); 5165 insertOrganization(rawContactId, values); 5166 5167 // And another data element 5168 insertEmail(rawContactId, "[email protected]", true, Email.TYPE_CUSTOM, "Custom"); 5169 5170 Uri filterUri = buildFilterUri("acme", true); 5171 5172 values.clear(); 5173 values.put(Contacts._ID, contactId); 5174 values.put(SearchSnippets.SNIPPET, "acmecorp"); 5175 assertContainsValues(filterUri, values); 5176 } 5177 5178 @Test testSearchSnippetEmail()5179 public void testSearchSnippetEmail() throws Exception { 5180 long rawContactId = RawContactUtil.createRawContact(mResolver); 5181 long contactId = queryContactId(rawContactId); 5182 ContentValues values = new ContentValues(); 5183 5184 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 5185 Uri dataUri = insertEmail(rawContactId, "[email protected]", true, Email.TYPE_CUSTOM, "Custom"); 5186 5187 Uri filterUri = buildFilterUri("acme", true); 5188 5189 values.clear(); 5190 values.put(Contacts._ID, contactId); 5191 values.put(SearchSnippets.SNIPPET, "[email protected]"); 5192 assertStoredValues(filterUri, values); 5193 } 5194 5195 @Test testCountPhoneNumberDigits()5196 public void testCountPhoneNumberDigits() { 5197 assertEquals(10, ContactsProvider2.countPhoneNumberDigits("86 (0) 5-55-12-34")); 5198 assertEquals(10, ContactsProvider2.countPhoneNumberDigits("860 555-1234")); 5199 assertEquals(3, ContactsProvider2.countPhoneNumberDigits("860")); 5200 assertEquals(10, ContactsProvider2.countPhoneNumberDigits("8605551234")); 5201 assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860555")); 5202 assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860 555")); 5203 assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860-555")); 5204 assertEquals(12, ContactsProvider2.countPhoneNumberDigits("+441234098765")); 5205 assertEquals(0, ContactsProvider2.countPhoneNumberDigits("44+1234098765")); 5206 assertEquals(0, ContactsProvider2.countPhoneNumberDigits("+441234098foo")); 5207 } 5208 5209 @Test testSearchSnippetPhone()5210 public void testSearchSnippetPhone() throws Exception { 5211 long rawContactId = RawContactUtil.createRawContact(mResolver); 5212 long contactId = queryContactId(rawContactId); 5213 ContentValues values = new ContentValues(); 5214 5215 DataUtil.insertStructuredName(mResolver, rawContactId, "Cave", "Johnson"); 5216 insertPhoneNumber(rawContactId, "(860) 555-1234"); 5217 5218 values.clear(); 5219 values.put(Contacts._ID, contactId); 5220 values.put(SearchSnippets.SNIPPET, "[(860) 555-1234]"); 5221 5222 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5223 Uri.encode("86 (0) 5-55-12-34")), values); 5224 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5225 Uri.encode("860 555-1234")), values); 5226 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5227 Uri.encode("860")), values); 5228 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5229 Uri.encode("8605551234")), values); 5230 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5231 Uri.encode("860555")), values); 5232 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5233 Uri.encode("860 555")), values); 5234 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 5235 Uri.encode("860-555")), values); 5236 } 5237 buildFilterUri(String query, boolean deferredSnippeting)5238 private Uri buildFilterUri(String query, boolean deferredSnippeting) { 5239 Uri.Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon() 5240 .appendPath(Uri.encode(query)); 5241 if (deferredSnippeting) { 5242 builder.appendQueryParameter(ContactsContract.DEFERRED_SNIPPETING, "1"); 5243 } 5244 return builder.build(); 5245 } 5246 createSnippetContentValues(long contactId, String snippet)5247 public ContentValues createSnippetContentValues(long contactId, String snippet) { 5248 final ContentValues values = new ContentValues(); 5249 values.clear(); 5250 values.put(Contacts._ID, contactId); 5251 values.put(SearchSnippets.SNIPPET, snippet); 5252 return values; 5253 } 5254 5255 @Test testSearchSnippetNickname()5256 public void testSearchSnippetNickname() throws Exception { 5257 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 5258 long contactId = queryContactId(rawContactId); 5259 ContentValues values = new ContentValues(); 5260 5261 Uri dataUri = insertNickname(rawContactId, "Incredible"); 5262 5263 Uri filterUri = buildFilterUri("inc", true); 5264 5265 values.clear(); 5266 values.put(Contacts._ID, contactId); 5267 values.put(SearchSnippets.SNIPPET, "Incredible"); 5268 assertStoredValues(filterUri, values); 5269 } 5270 5271 @Test testSearchSnippetEmptyForNameInDisplayName()5272 public void testSearchSnippetEmptyForNameInDisplayName() throws Exception { 5273 long rawContactId = RawContactUtil.createRawContact(mResolver); 5274 long contactId = queryContactId(rawContactId); 5275 DataUtil.insertStructuredName(mResolver, rawContactId, "Cave", "Johnson"); 5276 insertEmail(rawContactId, "[email protected]", true); 5277 5278 ContentValues snippet = createSnippetContentValues(contactId, "[email protected]"); 5279 5280 assertContainsValues(buildFilterUri("cave", true), snippet); 5281 assertContainsValues(buildFilterUri("john", true), snippet); 5282 } 5283 5284 @Test testSearchSnippetEmptyForNicknameInDisplayName()5285 public void testSearchSnippetEmptyForNicknameInDisplayName() throws Exception { 5286 long rawContactId = RawContactUtil.createRawContact(mResolver); 5287 long contactId = queryContactId(rawContactId); 5288 insertNickname(rawContactId, "Caveman"); 5289 insertEmail(rawContactId, "[email protected]", true); 5290 5291 ContentValues snippet = createSnippetContentValues(contactId, "[email protected]"); 5292 5293 assertContainsValues(buildFilterUri("cave", true), snippet); 5294 } 5295 5296 @Test testSearchSnippetEmptyForCompanyInDisplayName()5297 public void testSearchSnippetEmptyForCompanyInDisplayName() throws Exception { 5298 long rawContactId = RawContactUtil.createRawContact(mResolver); 5299 long contactId = queryContactId(rawContactId); 5300 ContentValues company = new ContentValues(); 5301 company.clear(); 5302 company.put(Organization.COMPANY, "Aperture Science"); 5303 company.put(Organization.TITLE, "President"); 5304 insertOrganization(rawContactId, company); 5305 insertEmail(rawContactId, "[email protected]", true); 5306 5307 ContentValues snippet = createSnippetContentValues(contactId, "aperturepresident"); 5308 5309 assertContainsValues(buildFilterUri("aperture", true), snippet); 5310 } 5311 5312 @Test testSearchSnippetEmptyForPhoneInDisplayName()5313 public void testSearchSnippetEmptyForPhoneInDisplayName() throws Exception { 5314 long rawContactId = RawContactUtil.createRawContact(mResolver); 5315 long contactId = queryContactId(rawContactId); 5316 insertPhoneNumber(rawContactId, "860-555-1234"); 5317 insertEmail(rawContactId, "[email protected]", true); 5318 5319 ContentValues snippet = createSnippetContentValues(contactId, "860-555-1234"); 5320 5321 assertContainsValues(buildFilterUri("860", true), snippet); 5322 } 5323 5324 @Test testSearchSnippetEmptyForEmailInDisplayName()5325 public void testSearchSnippetEmptyForEmailInDisplayName() throws Exception { 5326 long rawContactId = RawContactUtil.createRawContact(mResolver); 5327 long contactId = queryContactId(rawContactId); 5328 insertEmail(rawContactId, "[email protected]", true); 5329 insertNote(rawContactId, "Cave Johnson is president of Aperture Science"); 5330 5331 ContentValues snippet = createSnippetContentValues(contactId, 5332 "Cave Johnson is president of Aperture Science"); 5333 5334 assertContainsValues(buildFilterUri("cave", true), snippet); 5335 } 5336 5337 @Test testDisplayNameUpdateFromStructuredNameUpdate()5338 public void testDisplayNameUpdateFromStructuredNameUpdate() { 5339 long rawContactId = RawContactUtil.createRawContact(mResolver); 5340 Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, "Slinky", "Dog"); 5341 5342 long contactId = queryContactId(rawContactId); 5343 5344 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5345 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog"); 5346 5347 ContentValues values = new ContentValues(); 5348 values.putNull(StructuredName.FAMILY_NAME); 5349 5350 mResolver.update(nameUri, values, null, null); 5351 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky"); 5352 5353 values.putNull(StructuredName.GIVEN_NAME); 5354 5355 mResolver.update(nameUri, values, null, null); 5356 assertStoredValue(uri, Contacts.DISPLAY_NAME, null); 5357 5358 values.put(StructuredName.FAMILY_NAME, "Dog"); 5359 mResolver.update(nameUri, values, null, null); 5360 5361 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog"); 5362 } 5363 5364 @Test testInsertDataWithContentProviderOperations()5365 public void testInsertDataWithContentProviderOperations() throws Exception { 5366 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 5367 .withValues(new ContentValues()) 5368 .build(); 5369 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 5370 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 5371 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 5372 .withValue(StructuredName.GIVEN_NAME, "John") 5373 .withValue(StructuredName.FAMILY_NAME, "Doe") 5374 .build(); 5375 ContentProviderResult[] results = 5376 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2)); 5377 long contactId = queryContactId(ContentUris.parseId(results[0].uri)); 5378 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5379 assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe"); 5380 } 5381 5382 @Test testSendToVoicemailDefault()5383 public void testSendToVoicemailDefault() { 5384 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 5385 long contactId = queryContactId(rawContactId); 5386 5387 Cursor c = queryContact(contactId); 5388 assertTrue(c.moveToNext()); 5389 int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL)); 5390 assertEquals(0, sendToVoicemail); 5391 c.close(); 5392 } 5393 5394 @Test testSetSendToVoicemailAndRingtone()5395 public void testSetSendToVoicemailAndRingtone() { 5396 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 5397 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 5398 assertDirty(rawContactUri, true); 5399 clearDirty(rawContactUri); 5400 long contactId = queryContactId(rawContactId); 5401 5402 updateSendToVoicemailAndRingtone(contactId, true, "foo"); 5403 assertSendToVoicemailAndRingtone(contactId, true, "foo"); 5404 assertNetworkNotified(false); 5405 assertDirty(rawContactUri, false); 5406 assertMetadataDirty(rawContactUri, false); 5407 5408 updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar"); 5409 assertSendToVoicemailAndRingtone(contactId, false, "bar"); 5410 assertNetworkNotified(false); 5411 assertDirty(rawContactUri, false); 5412 assertMetadataDirty(rawContactUri, false); 5413 } 5414 5415 @Test testSendToVoicemailAndRingtoneAfterAggregation()5416 public void testSendToVoicemailAndRingtoneAfterAggregation() { 5417 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "a", "b"); 5418 long contactId1 = queryContactId(rawContactId1); 5419 updateSendToVoicemailAndRingtone(contactId1, true, "foo"); 5420 5421 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "c", "d"); 5422 long contactId2 = queryContactId(rawContactId2); 5423 updateSendToVoicemailAndRingtone(contactId2, true, "bar"); 5424 5425 // Aggregate them 5426 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 5427 rawContactId1, rawContactId2); 5428 5429 // Both contacts had "send to VM", the contact now has the same value 5430 assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar 5431 } 5432 5433 @Test testDoNotSendToVoicemailAfterAggregation()5434 public void testDoNotSendToVoicemailAfterAggregation() { 5435 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "e", "f"); 5436 long contactId1 = queryContactId(rawContactId1); 5437 updateSendToVoicemailAndRingtone(contactId1, true, null); 5438 5439 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "g", "h"); 5440 long contactId2 = queryContactId(rawContactId2); 5441 updateSendToVoicemailAndRingtone(contactId2, false, null); 5442 5443 // Aggregate them 5444 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 5445 rawContactId1, rawContactId2); 5446 5447 // Since one of the contacts had "don't send to VM" that setting wins for the aggregate 5448 assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null); 5449 } 5450 5451 @Test testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit()5452 public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() { 5453 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j"); 5454 long contactId1 = queryContactId(rawContactId1); 5455 updateSendToVoicemailAndRingtone(contactId1, true, "foo"); 5456 5457 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l"); 5458 long contactId2 = queryContactId(rawContactId2); 5459 updateSendToVoicemailAndRingtone(contactId2, false, "bar"); 5460 5461 // Aggregate them 5462 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 5463 rawContactId1, rawContactId2); 5464 5465 // Split them 5466 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 5467 rawContactId1, rawContactId2); 5468 5469 assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo"); 5470 assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar"); 5471 } 5472 5473 @Test testMarkMetadataDirtyAfterAggregation()5474 public void testMarkMetadataDirtyAfterAggregation() { 5475 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j"); 5476 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l"); 5477 Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 5478 Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); 5479 assertDirty(rawContactUri1, true); 5480 assertDirty(rawContactUri2, true); 5481 clearDirty(rawContactUri1); 5482 clearDirty(rawContactUri2); 5483 5484 // Aggregate them 5485 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 5486 rawContactId1, rawContactId2); 5487 5488 assertDirty(rawContactUri1, false); 5489 assertDirty(rawContactUri2, false); 5490 assertMetadataDirty(rawContactUri1, false); 5491 assertMetadataDirty(rawContactUri2, false); 5492 assertNetworkNotified(false); 5493 } 5494 5495 @Test testStatusUpdateInsert()5496 public void testStatusUpdateInsert() { 5497 long rawContactId = RawContactUtil.createRawContact(mResolver); 5498 Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5499 long dataId = ContentUris.parseId(imUri); 5500 5501 ContentValues values = new ContentValues(); 5502 values.put(StatusUpdates.DATA_ID, dataId); 5503 values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM); 5504 values.putNull(StatusUpdates.CUSTOM_PROTOCOL); 5505 values.put(StatusUpdates.IM_HANDLE, "aim"); 5506 values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE); 5507 values.put(StatusUpdates.STATUS, "Hiding"); 5508 values.put(StatusUpdates.STATUS_TIMESTAMP, 100); 5509 values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c"); 5510 values.put(StatusUpdates.STATUS_ICON, 1234); 5511 values.put(StatusUpdates.STATUS_LABEL, 2345); 5512 5513 Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values); 5514 5515 assertStoredValues(resultUri, values); 5516 5517 long contactId = queryContactId(rawContactId); 5518 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5519 5520 values.clear(); 5521 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 5522 values.put(Contacts.CONTACT_STATUS, "Hiding"); 5523 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100); 5524 values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c"); 5525 values.put(Contacts.CONTACT_STATUS_ICON, 1234); 5526 values.put(Contacts.CONTACT_STATUS_LABEL, 2345); 5527 5528 assertStoredValues(contactUri, values); 5529 5530 values.clear(); 5531 values.put(StatusUpdates.DATA_ID, dataId); 5532 values.put(StatusUpdates.STATUS, "Cloaked"); 5533 values.put(StatusUpdates.STATUS_TIMESTAMP, 200); 5534 values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f"); 5535 values.put(StatusUpdates.STATUS_ICON, 4321); 5536 values.put(StatusUpdates.STATUS_LABEL, 5432); 5537 mResolver.insert(StatusUpdates.CONTENT_URI, values); 5538 5539 values.clear(); 5540 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 5541 values.put(Contacts.CONTACT_STATUS, "Cloaked"); 5542 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200); 5543 values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f"); 5544 values.put(Contacts.CONTACT_STATUS_ICON, 4321); 5545 values.put(Contacts.CONTACT_STATUS_LABEL, 5432); 5546 assertStoredValues(contactUri, values); 5547 } 5548 5549 @Test testStatusUpdateInferAttribution()5550 public void testStatusUpdateInferAttribution() { 5551 long rawContactId = RawContactUtil.createRawContact(mResolver); 5552 Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5553 long dataId = ContentUris.parseId(imUri); 5554 5555 ContentValues values = new ContentValues(); 5556 values.put(StatusUpdates.DATA_ID, dataId); 5557 values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM); 5558 values.put(StatusUpdates.IM_HANDLE, "aim"); 5559 values.put(StatusUpdates.STATUS, "Hiding"); 5560 5561 Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values); 5562 5563 values.clear(); 5564 values.put(StatusUpdates.DATA_ID, dataId); 5565 values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim); 5566 values.put(StatusUpdates.STATUS, "Hiding"); 5567 5568 assertStoredValues(resultUri, values); 5569 } 5570 5571 @Test testStatusUpdateMatchingImOrEmail()5572 public void testStatusUpdateMatchingImOrEmail() { 5573 long rawContactId = RawContactUtil.createRawContact(mResolver); 5574 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5575 insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im"); 5576 insertEmail(rawContactId, "[email protected]"); 5577 5578 // Match on IM (standard) 5579 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 5580 StatusUpdates.CAPABILITY_HAS_CAMERA); 5581 5582 // Match on IM (custom) 5583 insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle", 5584 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 5585 5586 // Match on Email 5587 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", StatusUpdates.AWAY, "Away", 5588 StatusUpdates.CAPABILITY_HAS_VOICE); 5589 5590 // No match 5591 insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away", 5592 StatusUpdates.CAPABILITY_HAS_CAMERA); 5593 5594 Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] { 5595 StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL, 5596 StatusUpdates.PRESENCE, StatusUpdates.STATUS}, 5597 PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID); 5598 assertTrue(c.moveToNext()); 5599 assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available"); 5600 assertTrue(c.moveToNext()); 5601 assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle"); 5602 assertTrue(c.moveToNext()); 5603 assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away"); 5604 assertFalse(c.moveToNext()); 5605 c.close(); 5606 5607 long contactId = queryContactId(rawContactId); 5608 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5609 5610 ContentValues values = new ContentValues(); 5611 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 5612 values.put(Contacts.CONTACT_STATUS, "Available"); 5613 assertStoredValuesWithProjection(contactUri, values); 5614 } 5615 5616 @Test testStatusUpdateUpdateAndDelete()5617 public void testStatusUpdateUpdateAndDelete() { 5618 long rawContactId = RawContactUtil.createRawContact(mResolver); 5619 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5620 5621 long contactId = queryContactId(rawContactId); 5622 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5623 5624 ContentValues values = new ContentValues(); 5625 values.putNull(Contacts.CONTACT_PRESENCE); 5626 values.putNull(Contacts.CONTACT_STATUS); 5627 assertStoredValuesWithProjection(contactUri, values); 5628 5629 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY", 5630 StatusUpdates.CAPABILITY_HAS_CAMERA); 5631 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY", 5632 StatusUpdates.CAPABILITY_HAS_CAMERA); 5633 Uri statusUri = 5634 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 5635 StatusUpdates.CAPABILITY_HAS_CAMERA); 5636 long statusId = ContentUris.parseId(statusUri); 5637 5638 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 5639 values.put(Contacts.CONTACT_STATUS, "Available"); 5640 assertStoredValuesWithProjection(contactUri, values); 5641 5642 // update status_updates table to set new values for 5643 // status_updates.status 5644 // status_updates.status_ts 5645 // presence 5646 long updatedTs = 200; 5647 String testUpdate = "test_update"; 5648 String selection = StatusUpdates.DATA_ID + "=" + statusId; 5649 values.clear(); 5650 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 5651 values.put(StatusUpdates.STATUS, testUpdate); 5652 values.put(StatusUpdates.PRESENCE, "presence_test"); 5653 mResolver.update(StatusUpdates.CONTENT_URI, values, 5654 StatusUpdates.DATA_ID + "=" + statusId, null); 5655 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 5656 5657 // update status_updates table to set new values for columns in status_updates table ONLY 5658 // i.e., no rows in presence table are to be updated. 5659 updatedTs = 300; 5660 testUpdate = "test_update_new"; 5661 selection = StatusUpdates.DATA_ID + "=" + statusId; 5662 values.clear(); 5663 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 5664 values.put(StatusUpdates.STATUS, testUpdate); 5665 mResolver.update(StatusUpdates.CONTENT_URI, values, 5666 StatusUpdates.DATA_ID + "=" + statusId, null); 5667 // make sure the presence column value is still the old value 5668 values.put(StatusUpdates.PRESENCE, "presence_test"); 5669 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 5670 5671 // update status_updates table to set new values for columns in presence table ONLY 5672 // i.e., no rows in status_updates table are to be updated. 5673 selection = StatusUpdates.DATA_ID + "=" + statusId; 5674 values.clear(); 5675 values.put(StatusUpdates.PRESENCE, "presence_test_new"); 5676 mResolver.update(StatusUpdates.CONTENT_URI, values, 5677 StatusUpdates.DATA_ID + "=" + statusId, null); 5678 // make sure the status_updates table is not updated 5679 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 5680 values.put(StatusUpdates.STATUS, testUpdate); 5681 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 5682 5683 // effect "delete status_updates" operation and expect the following 5684 // data deleted from status_updates table 5685 // presence set to null 5686 mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null); 5687 values.clear(); 5688 values.putNull(Contacts.CONTACT_PRESENCE); 5689 assertStoredValuesWithProjection(contactUri, values); 5690 } 5691 5692 @Test testStatusUpdateUpdateToNull()5693 public void testStatusUpdateUpdateToNull() { 5694 long rawContactId = RawContactUtil.createRawContact(mResolver); 5695 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5696 5697 long contactId = queryContactId(rawContactId); 5698 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5699 5700 ContentValues values = new ContentValues(); 5701 Uri statusUri = 5702 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 5703 StatusUpdates.CAPABILITY_HAS_CAMERA); 5704 long statusId = ContentUris.parseId(statusUri); 5705 5706 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 5707 values.put(Contacts.CONTACT_STATUS, "Available"); 5708 assertStoredValuesWithProjection(contactUri, values); 5709 5710 values.clear(); 5711 values.putNull(StatusUpdates.PRESENCE); 5712 mResolver.update(StatusUpdates.CONTENT_URI, values, 5713 StatusUpdates.DATA_ID + "=" + statusId, null); 5714 5715 values.clear(); 5716 values.putNull(Contacts.CONTACT_PRESENCE); 5717 values.put(Contacts.CONTACT_STATUS, "Available"); 5718 assertStoredValuesWithProjection(contactUri, values); 5719 } 5720 5721 @Test testStatusUpdateWithTimestamp()5722 public void testStatusUpdateWithTimestamp() { 5723 long rawContactId = RawContactUtil.createRawContact(mResolver); 5724 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 5725 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 5726 5727 long contactId = queryContactId(rawContactId); 5728 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5729 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80, 5730 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 5731 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100, 5732 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 5733 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90, 5734 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 5735 5736 // Should return the latest status 5737 ContentValues values = new ContentValues(); 5738 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100); 5739 values.put(Contacts.CONTACT_STATUS, "Available"); 5740 assertStoredValuesWithProjection(contactUri, values); 5741 } 5742 assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence, String status)5743 private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence, 5744 String status) { 5745 ContentValues values = new ContentValues(); 5746 values.put(StatusUpdates.PROTOCOL, protocol); 5747 values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol); 5748 values.put(StatusUpdates.PRESENCE, presence); 5749 values.put(StatusUpdates.STATUS, status); 5750 assertCursorValues(c, values); 5751 } 5752 5753 // Stream item query test cases. 5754 5755 @Test testQueryStreamItemsByRawContactId()5756 public void testQueryStreamItemsByRawContactId() { 5757 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 5758 ContentValues values = buildGenericStreamItemValues(); 5759 insertStreamItem(rawContactId, values, mAccount); 5760 assertStoredValues( 5761 Uri.withAppendedPath( 5762 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 5763 RawContacts.StreamItems.CONTENT_DIRECTORY), 5764 values); 5765 } 5766 5767 @Test testQueryStreamItemsByContactId()5768 public void testQueryStreamItemsByContactId() { 5769 long rawContactId = RawContactUtil.createRawContact(mResolver); 5770 long contactId = queryContactId(rawContactId); 5771 ContentValues values = buildGenericStreamItemValues(); 5772 insertStreamItem(rawContactId, values, null); 5773 assertStoredValues( 5774 Uri.withAppendedPath( 5775 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 5776 Contacts.StreamItems.CONTENT_DIRECTORY), 5777 values); 5778 } 5779 5780 @Test testQueryStreamItemsByLookupKey()5781 public void testQueryStreamItemsByLookupKey() { 5782 long rawContactId = RawContactUtil.createRawContact(mResolver); 5783 long contactId = queryContactId(rawContactId); 5784 String lookupKey = queryLookupKey(contactId); 5785 ContentValues values = buildGenericStreamItemValues(); 5786 insertStreamItem(rawContactId, values, null); 5787 assertStoredValues( 5788 Uri.withAppendedPath( 5789 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), 5790 Contacts.StreamItems.CONTENT_DIRECTORY), 5791 values); 5792 } 5793 5794 @Test testQueryStreamItemsByLookupKeyAndContactId()5795 public void testQueryStreamItemsByLookupKeyAndContactId() { 5796 long rawContactId = RawContactUtil.createRawContact(mResolver); 5797 long contactId = queryContactId(rawContactId); 5798 String lookupKey = queryLookupKey(contactId); 5799 ContentValues values = buildGenericStreamItemValues(); 5800 insertStreamItem(rawContactId, values, null); 5801 assertStoredValues( 5802 Uri.withAppendedPath( 5803 ContentUris.withAppendedId( 5804 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), 5805 contactId), 5806 Contacts.StreamItems.CONTENT_DIRECTORY), 5807 values); 5808 } 5809 5810 @Test testQueryStreamItems()5811 public void testQueryStreamItems() { 5812 long rawContactId = RawContactUtil.createRawContact(mResolver); 5813 ContentValues values = buildGenericStreamItemValues(); 5814 insertStreamItem(rawContactId, values, null); 5815 assertStoredValues(StreamItems.CONTENT_URI, values); 5816 } 5817 5818 @Test testQueryStreamItemsWithSelection()5819 public void testQueryStreamItemsWithSelection() { 5820 long rawContactId = RawContactUtil.createRawContact(mResolver); 5821 ContentValues firstValues = buildGenericStreamItemValues(); 5822 insertStreamItem(rawContactId, firstValues, null); 5823 5824 ContentValues secondValues = buildGenericStreamItemValues(); 5825 secondValues.put(StreamItems.TEXT, "Goodbye world"); 5826 insertStreamItem(rawContactId, secondValues, null); 5827 5828 // Select only the first stream item. 5829 assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 5830 new String[]{"Hello world"}, firstValues); 5831 5832 // Select only the second stream item. 5833 assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 5834 new String[]{"Goodbye world"}, secondValues); 5835 } 5836 5837 @Test testQueryStreamItemById()5838 public void testQueryStreamItemById() { 5839 long rawContactId = RawContactUtil.createRawContact(mResolver); 5840 ContentValues firstValues = buildGenericStreamItemValues(); 5841 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 5842 long firstStreamItemId = ContentUris.parseId(resultUri); 5843 5844 ContentValues secondValues = buildGenericStreamItemValues(); 5845 secondValues.put(StreamItems.TEXT, "Goodbye world"); 5846 resultUri = insertStreamItem(rawContactId, secondValues, null); 5847 long secondStreamItemId = ContentUris.parseId(resultUri); 5848 5849 // Select only the first stream item. 5850 assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 5851 firstValues); 5852 5853 // Select only the second stream item. 5854 assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 5855 secondValues); 5856 } 5857 5858 // Stream item photo insertion + query test cases. 5859 5860 @Test testQueryStreamItemPhotoWithSelection()5861 public void testQueryStreamItemPhotoWithSelection() { 5862 long rawContactId = RawContactUtil.createRawContact(mResolver); 5863 ContentValues values = buildGenericStreamItemValues(); 5864 Uri resultUri = insertStreamItem(rawContactId, values, null); 5865 long streamItemId = ContentUris.parseId(resultUri); 5866 5867 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 5868 insertStreamItemPhoto(streamItemId, photo1Values, null); 5869 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 5870 ContentValues photo2Values = buildGenericStreamItemPhotoValues(2); 5871 insertStreamItemPhoto(streamItemId, photo2Values, null); 5872 5873 // Select only the first photo. 5874 assertStoredValues(StreamItems.CONTENT_PHOTO_URI, StreamItemPhotos.SORT_INDEX + "=?", 5875 new String[]{"1"}, photo1Values); 5876 } 5877 5878 @Test testQueryStreamItemPhotoByStreamItemId()5879 public void testQueryStreamItemPhotoByStreamItemId() { 5880 long rawContactId = RawContactUtil.createRawContact(mResolver); 5881 5882 // Insert a first stream item. 5883 ContentValues firstValues = buildGenericStreamItemValues(); 5884 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 5885 long firstStreamItemId = ContentUris.parseId(resultUri); 5886 5887 // Insert a second stream item. 5888 ContentValues secondValues = buildGenericStreamItemValues(); 5889 resultUri = insertStreamItem(rawContactId, secondValues, null); 5890 long secondStreamItemId = ContentUris.parseId(resultUri); 5891 5892 // Add a photo to the first stream item. 5893 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 5894 insertStreamItemPhoto(firstStreamItemId, photo1Values, null); 5895 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 5896 5897 // Add a photo to the second stream item. 5898 ContentValues photo2Values = buildGenericStreamItemPhotoValues(1); 5899 photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 5900 R.drawable.nebula, PhotoSize.ORIGINAL)); 5901 insertStreamItemPhoto(secondStreamItemId, photo2Values, null); 5902 photo2Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 5903 5904 // Select only the photos from the second stream item. 5905 assertStoredValues(Uri.withAppendedPath( 5906 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 5907 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), photo2Values); 5908 } 5909 5910 @Test testQueryStreamItemPhotoByStreamItemPhotoId()5911 public void testQueryStreamItemPhotoByStreamItemPhotoId() { 5912 long rawContactId = RawContactUtil.createRawContact(mResolver); 5913 5914 // Insert a first stream item. 5915 ContentValues firstValues = buildGenericStreamItemValues(); 5916 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 5917 long firstStreamItemId = ContentUris.parseId(resultUri); 5918 5919 // Insert a second stream item. 5920 ContentValues secondValues = buildGenericStreamItemValues(); 5921 resultUri = insertStreamItem(rawContactId, secondValues, null); 5922 long secondStreamItemId = ContentUris.parseId(resultUri); 5923 5924 // Add a photo to the first stream item. 5925 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 5926 resultUri = insertStreamItemPhoto(firstStreamItemId, photo1Values, null); 5927 long firstPhotoId = ContentUris.parseId(resultUri); 5928 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 5929 5930 // Add a photo to the second stream item. 5931 ContentValues photo2Values = buildGenericStreamItemPhotoValues(1); 5932 photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 5933 R.drawable.galaxy, PhotoSize.ORIGINAL)); 5934 resultUri = insertStreamItemPhoto(secondStreamItemId, photo2Values, null); 5935 long secondPhotoId = ContentUris.parseId(resultUri); 5936 photo2Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 5937 5938 // Select the first photo. 5939 assertStoredValues(ContentUris.withAppendedId( 5940 Uri.withAppendedPath( 5941 ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 5942 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 5943 firstPhotoId), 5944 photo1Values); 5945 5946 // Select the second photo. 5947 assertStoredValues(ContentUris.withAppendedId( 5948 Uri.withAppendedPath( 5949 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 5950 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 5951 secondPhotoId), 5952 photo2Values); 5953 } 5954 5955 // Stream item insertion test cases. 5956 5957 @Test testInsertStreamItemInProfileRequiresWriteProfileAccess()5958 public void testInsertStreamItemInProfileRequiresWriteProfileAccess() { 5959 long profileRawContactId = createBasicProfileContact(new ContentValues()); 5960 5961 // Try inserting a stream item. It should still succeed even without the profile permission. 5962 ContentValues values = buildGenericStreamItemValues(); 5963 insertStreamItem(profileRawContactId, values, null); 5964 } 5965 5966 @Test testInsertStreamItemWithContentValues()5967 public void testInsertStreamItemWithContentValues() { 5968 long rawContactId = RawContactUtil.createRawContact(mResolver); 5969 ContentValues values = buildGenericStreamItemValues(); 5970 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 5971 mResolver.insert(StreamItems.CONTENT_URI, values); 5972 assertStoredValues(Uri.withAppendedPath( 5973 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 5974 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 5975 } 5976 5977 @Test testInsertStreamItemOverLimit()5978 public void testInsertStreamItemOverLimit() { 5979 long rawContactId = RawContactUtil.createRawContact(mResolver); 5980 ContentValues values = buildGenericStreamItemValues(); 5981 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 5982 5983 List<Long> streamItemIds = Lists.newArrayList(); 5984 5985 // Insert MAX + 1 stream items. 5986 long baseTime = System.currentTimeMillis(); 5987 for (int i = 0; i < 6; i++) { 5988 values.put(StreamItems.TIMESTAMP, baseTime + i); 5989 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 5990 streamItemIds.add(ContentUris.parseId(resultUri)); 5991 } 5992 Long doomedStreamItemId = streamItemIds.get(0); 5993 5994 // There should only be MAX items. The oldest one should have been cleaned up. 5995 Cursor c = mResolver.query( 5996 Uri.withAppendedPath( 5997 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 5998 RawContacts.StreamItems.CONTENT_DIRECTORY), 5999 new String[]{StreamItems._ID}, null, null, null); 6000 try { 6001 while(c.moveToNext()) { 6002 long streamItemId = c.getLong(0); 6003 streamItemIds.remove(streamItemId); 6004 } 6005 } finally { 6006 c.close(); 6007 } 6008 6009 assertEquals(1, streamItemIds.size()); 6010 } 6011 6012 @Test testInsertStreamItemOlderThanOldestInLimit()6013 public void testInsertStreamItemOlderThanOldestInLimit() { 6014 long rawContactId = RawContactUtil.createRawContact(mResolver); 6015 ContentValues values = buildGenericStreamItemValues(); 6016 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 6017 6018 // Insert MAX stream items. 6019 long baseTime = System.currentTimeMillis(); 6020 for (int i = 0; i < 5; i++) { 6021 values.put(StreamItems.TIMESTAMP, baseTime + i); 6022 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 6023 assertNotSame("Expected non-0 stream item ID to be inserted", 6024 0L, ContentUris.parseId(resultUri)); 6025 } 6026 6027 // Now try to insert a stream item that's older. It should be deleted immediately 6028 // and return an ID of 0. 6029 values.put(StreamItems.TIMESTAMP, baseTime - 1); 6030 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 6031 assertEquals(0L, ContentUris.parseId(resultUri)); 6032 } 6033 6034 // Stream item photo insertion test cases. 6035 6036 @Test testInsertStreamItemsAndPhotosInBatch()6037 public void testInsertStreamItemsAndPhotosInBatch() throws Exception { 6038 long rawContactId = RawContactUtil.createRawContact(mResolver); 6039 ContentValues streamItemValues = buildGenericStreamItemValues(); 6040 ContentValues streamItemPhotoValues = buildGenericStreamItemPhotoValues(0); 6041 6042 ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); 6043 ops.add(ContentProviderOperation.newInsert( 6044 Uri.withAppendedPath( 6045 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6046 RawContacts.StreamItems.CONTENT_DIRECTORY)) 6047 .withValues(streamItemValues).build()); 6048 for (int i = 0; i < 5; i++) { 6049 streamItemPhotoValues.put(StreamItemPhotos.SORT_INDEX, i); 6050 ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI) 6051 .withValues(streamItemPhotoValues) 6052 .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, 0) 6053 .build()); 6054 } 6055 mResolver.applyBatch(ContactsContract.AUTHORITY, ops); 6056 6057 // Check that all five photos were inserted under the raw contact. 6058 Cursor c = mResolver.query(StreamItems.CONTENT_URI, new String[]{StreamItems._ID}, 6059 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)}, 6060 null); 6061 long streamItemId = 0; 6062 try { 6063 assertEquals(1, c.getCount()); 6064 c.moveToFirst(); 6065 streamItemId = c.getLong(0); 6066 } finally { 6067 c.close(); 6068 } 6069 6070 c = mResolver.query(Uri.withAppendedPath( 6071 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 6072 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 6073 new String[]{StreamItemPhotos._ID, StreamItemPhotos.PHOTO_URI}, 6074 null, null, null); 6075 try { 6076 assertEquals(5, c.getCount()); 6077 byte[] expectedPhotoBytes = loadPhotoFromResource( 6078 R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO); 6079 while (c.moveToNext()) { 6080 String photoUri = c.getString(1); 6081 EvenMoreAsserts.assertImageRawData(getContext(), 6082 expectedPhotoBytes, mResolver.openInputStream(Uri.parse(photoUri))); 6083 } 6084 } finally { 6085 c.close(); 6086 } 6087 } 6088 6089 // Stream item update test cases. 6090 6091 @Test testUpdateStreamItemById()6092 public void testUpdateStreamItemById() { 6093 long rawContactId = RawContactUtil.createRawContact(mResolver); 6094 ContentValues values = buildGenericStreamItemValues(); 6095 Uri resultUri = insertStreamItem(rawContactId, values, null); 6096 long streamItemId = ContentUris.parseId(resultUri); 6097 values.put(StreamItems.TEXT, "Goodbye world"); 6098 mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), values, 6099 null, null); 6100 assertStoredValues(Uri.withAppendedPath( 6101 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6102 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 6103 } 6104 6105 @Test testUpdateStreamItemWithContentValues()6106 public void testUpdateStreamItemWithContentValues() { 6107 long rawContactId = RawContactUtil.createRawContact(mResolver); 6108 ContentValues values = buildGenericStreamItemValues(); 6109 Uri resultUri = insertStreamItem(rawContactId, values, null); 6110 long streamItemId = ContentUris.parseId(resultUri); 6111 values.put(StreamItems._ID, streamItemId); 6112 values.put(StreamItems.TEXT, "Goodbye world"); 6113 mResolver.update(StreamItems.CONTENT_URI, values, null, null); 6114 assertStoredValues(Uri.withAppendedPath( 6115 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6116 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 6117 } 6118 6119 // Stream item photo update test cases. 6120 6121 @Test testUpdateStreamItemPhotoById()6122 public void testUpdateStreamItemPhotoById() throws IOException { 6123 long rawContactId = RawContactUtil.createRawContact(mResolver); 6124 ContentValues values = buildGenericStreamItemValues(); 6125 Uri resultUri = insertStreamItem(rawContactId, values, null); 6126 long streamItemId = ContentUris.parseId(resultUri); 6127 ContentValues photoValues = buildGenericStreamItemPhotoValues(1); 6128 resultUri = insertStreamItemPhoto(streamItemId, photoValues, null); 6129 long streamItemPhotoId = ContentUris.parseId(resultUri); 6130 6131 photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 6132 R.drawable.nebula, PhotoSize.ORIGINAL)); 6133 Uri photoUri = 6134 ContentUris.withAppendedId( 6135 Uri.withAppendedPath( 6136 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 6137 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 6138 streamItemPhotoId); 6139 mResolver.update(photoUri, photoValues, null, null); 6140 photoValues.remove(StreamItemPhotos.PHOTO); // Removed during processing. 6141 assertStoredValues(photoUri, photoValues); 6142 6143 // Check that the photo stored is the expected one. 6144 String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI); 6145 EvenMoreAsserts.assertImageRawData(getContext(), 6146 loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO), 6147 mResolver.openInputStream(Uri.parse(displayPhotoUri))); 6148 } 6149 6150 @Test testUpdateStreamItemPhotoWithContentValues()6151 public void testUpdateStreamItemPhotoWithContentValues() throws IOException { 6152 long rawContactId = RawContactUtil.createRawContact(mResolver); 6153 ContentValues values = buildGenericStreamItemValues(); 6154 Uri resultUri = insertStreamItem(rawContactId, values, null); 6155 long streamItemId = ContentUris.parseId(resultUri); 6156 ContentValues photoValues = buildGenericStreamItemPhotoValues(1); 6157 resultUri = insertStreamItemPhoto(streamItemId, photoValues, null); 6158 long streamItemPhotoId = ContentUris.parseId(resultUri); 6159 6160 photoValues.put(StreamItemPhotos._ID, streamItemPhotoId); 6161 photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 6162 R.drawable.nebula, PhotoSize.ORIGINAL)); 6163 Uri photoUri = 6164 Uri.withAppendedPath( 6165 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 6166 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY); 6167 mResolver.update(photoUri, photoValues, null, null); 6168 photoValues.remove(StreamItemPhotos.PHOTO); // Removed during processing. 6169 assertStoredValues(photoUri, photoValues); 6170 6171 // Check that the photo stored is the expected one. 6172 String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI); 6173 EvenMoreAsserts.assertImageRawData(getContext(), 6174 loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO), 6175 mResolver.openInputStream(Uri.parse(displayPhotoUri))); 6176 } 6177 6178 // Stream item deletion test cases. 6179 6180 @Test testDeleteStreamItemById()6181 public void testDeleteStreamItemById() { 6182 long rawContactId = RawContactUtil.createRawContact(mResolver); 6183 ContentValues firstValues = buildGenericStreamItemValues(); 6184 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 6185 long firstStreamItemId = ContentUris.parseId(resultUri); 6186 6187 ContentValues secondValues = buildGenericStreamItemValues(); 6188 secondValues.put(StreamItems.TEXT, "Goodbye world"); 6189 insertStreamItem(rawContactId, secondValues, null); 6190 6191 // Delete the first stream item. 6192 mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 6193 null, null); 6194 6195 // Check that only the second item remains. 6196 assertStoredValues(Uri.withAppendedPath( 6197 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6198 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues); 6199 } 6200 6201 @Test testDeleteStreamItemWithSelection()6202 public void testDeleteStreamItemWithSelection() { 6203 long rawContactId = RawContactUtil.createRawContact(mResolver); 6204 ContentValues firstValues = buildGenericStreamItemValues(); 6205 insertStreamItem(rawContactId, firstValues, null); 6206 6207 ContentValues secondValues = buildGenericStreamItemValues(); 6208 secondValues.put(StreamItems.TEXT, "Goodbye world"); 6209 insertStreamItem(rawContactId, secondValues, null); 6210 6211 // Delete the first stream item with a custom selection. 6212 mResolver.delete(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 6213 new String[]{"Hello world"}); 6214 6215 // Check that only the second item remains. 6216 assertStoredValues(Uri.withAppendedPath( 6217 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6218 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues); 6219 } 6220 6221 // Stream item photo deletion test cases. 6222 6223 @Test testDeleteStreamItemPhotoById()6224 public void testDeleteStreamItemPhotoById() { 6225 long rawContactId = RawContactUtil.createRawContact(mResolver); 6226 long streamItemId = ContentUris.parseId( 6227 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 6228 long streamItemPhotoId = ContentUris.parseId( 6229 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null)); 6230 mResolver.delete( 6231 ContentUris.withAppendedId( 6232 Uri.withAppendedPath( 6233 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 6234 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 6235 streamItemPhotoId), null, null); 6236 6237 Cursor c = mResolver.query(StreamItems.CONTENT_PHOTO_URI, 6238 new String[]{StreamItemPhotos._ID}, 6239 StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{String.valueOf(streamItemId)}, 6240 null); 6241 try { 6242 assertEquals("Expected photo to be deleted.", 0, c.getCount()); 6243 } finally { 6244 c.close(); 6245 } 6246 } 6247 6248 @Test testDeleteStreamItemPhotoWithSelection()6249 public void testDeleteStreamItemPhotoWithSelection() { 6250 long rawContactId = RawContactUtil.createRawContact(mResolver); 6251 long streamItemId = ContentUris.parseId( 6252 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 6253 ContentValues firstPhotoValues = buildGenericStreamItemPhotoValues(0); 6254 ContentValues secondPhotoValues = buildGenericStreamItemPhotoValues(1); 6255 insertStreamItemPhoto(streamItemId, firstPhotoValues, null); 6256 firstPhotoValues.remove(StreamItemPhotos.PHOTO); // Removed while processing. 6257 insertStreamItemPhoto(streamItemId, secondPhotoValues, null); 6258 Uri photoUri = Uri.withAppendedPath( 6259 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 6260 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY); 6261 mResolver.delete(photoUri, StreamItemPhotos.SORT_INDEX + "=1", null); 6262 6263 assertStoredValues(photoUri, firstPhotoValues); 6264 } 6265 6266 @Test testDeleteStreamItemsWhenRawContactDeleted()6267 public void testDeleteStreamItemsWhenRawContactDeleted() { 6268 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 6269 Uri streamItemUri = insertStreamItem(rawContactId, 6270 buildGenericStreamItemValues(), mAccount); 6271 Uri streamItemPhotoUri = insertStreamItemPhoto(ContentUris.parseId(streamItemUri), 6272 buildGenericStreamItemPhotoValues(0), mAccount); 6273 mResolver.delete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 6274 null, null); 6275 6276 ContentValues[] emptyValues = new ContentValues[0]; 6277 6278 // The stream item and its photo should be gone. 6279 assertStoredValues(streamItemUri, emptyValues); 6280 assertStoredValues(streamItemPhotoUri, emptyValues); 6281 } 6282 6283 @Test testQueryStreamItemLimit()6284 public void testQueryStreamItemLimit() { 6285 ContentValues values = new ContentValues(); 6286 values.put(StreamItems.MAX_ITEMS, 5); 6287 assertStoredValues(StreamItems.CONTENT_LIMIT_URI, values); 6288 } 6289 6290 // Tests for inserting or updating stream items as a side-effect of making status updates 6291 // (forward-compatibility of status updates into the new social stream API). 6292 6293 @Test testStreamItemInsertedOnStatusUpdate()6294 public void testStreamItemInsertedOnStatusUpdate() { 6295 6296 // This method of creating a raw contact automatically inserts a status update with 6297 // the status message "hacking". 6298 ContentValues values = new ContentValues(); 6299 long rawContactId = createRawContact(values, "18004664411", 6300 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 6301 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 6302 StatusUpdates.CAPABILITY_HAS_VOICE); 6303 6304 ContentValues expectedValues = new ContentValues(); 6305 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 6306 expectedValues.put(StreamItems.TEXT, "hacking"); 6307 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 6308 .appendPath(String.valueOf(rawContactId)) 6309 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 6310 expectedValues); 6311 } 6312 6313 @Test testStreamItemInsertedOnStatusUpdate_HtmlQuoting()6314 public void testStreamItemInsertedOnStatusUpdate_HtmlQuoting() { 6315 6316 // This method of creating a raw contact automatically inserts a status update with 6317 // the status message "hacking". 6318 ContentValues values = new ContentValues(); 6319 long rawContactId = createRawContact(values, "18004664411", 6320 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 6321 StatusUpdates.CAPABILITY_HAS_VOICE); 6322 6323 // Insert a new status update for the raw contact. 6324 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 6325 StatusUpdates.INVISIBLE, "& <b> test '", StatusUpdates.CAPABILITY_HAS_VOICE); 6326 6327 ContentValues expectedValues = new ContentValues(); 6328 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 6329 expectedValues.put(StreamItems.TEXT, "& <b> test &#39;"); 6330 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 6331 .appendPath(String.valueOf(rawContactId)) 6332 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 6333 expectedValues); 6334 } 6335 6336 @Test testStreamItemUpdatedOnSecondStatusUpdate()6337 public void testStreamItemUpdatedOnSecondStatusUpdate() { 6338 6339 // This method of creating a raw contact automatically inserts a status update with 6340 // the status message "hacking". 6341 ContentValues values = new ContentValues(); 6342 int chatMode = StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 6343 StatusUpdates.CAPABILITY_HAS_VOICE; 6344 long rawContactId = createRawContact(values, "18004664411", 6345 "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, chatMode); 6346 6347 // Insert a new status update for the raw contact. 6348 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 6349 StatusUpdates.INVISIBLE, "finished hacking", chatMode); 6350 6351 ContentValues expectedValues = new ContentValues(); 6352 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 6353 expectedValues.put(StreamItems.TEXT, "finished hacking"); 6354 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 6355 .appendPath(String.valueOf(rawContactId)) 6356 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 6357 expectedValues); 6358 } 6359 buildGenericStreamItemValues()6360 private ContentValues buildGenericStreamItemValues() { 6361 ContentValues values = new ContentValues(); 6362 values.put(StreamItems.TEXT, "Hello world"); 6363 values.put(StreamItems.TIMESTAMP, System.currentTimeMillis()); 6364 values.put(StreamItems.COMMENTS, "Reshared by 123 others"); 6365 return values; 6366 } 6367 buildGenericStreamItemPhotoValues(int sortIndex)6368 private ContentValues buildGenericStreamItemPhotoValues(int sortIndex) { 6369 ContentValues values = new ContentValues(); 6370 values.put(StreamItemPhotos.SORT_INDEX, sortIndex); 6371 values.put(StreamItemPhotos.PHOTO, 6372 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL)); 6373 return values; 6374 } 6375 6376 @Test testSingleStatusUpdateRowPerContact()6377 public void testSingleStatusUpdateRowPerContact() { 6378 int protocol1 = Im.PROTOCOL_GOOGLE_TALK; 6379 String handle1 = "[email protected]"; 6380 6381 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 6382 insertImHandle(rawContactId1, protocol1, null, handle1); 6383 6384 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green", 6385 StatusUpdates.CAPABILITY_HAS_CAMERA); 6386 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow", 6387 StatusUpdates.CAPABILITY_HAS_CAMERA); 6388 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red", 6389 StatusUpdates.CAPABILITY_HAS_CAMERA); 6390 6391 Cursor c = queryContact(queryContactId(rawContactId1), 6392 new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS}); 6393 assertEquals(1, c.getCount()); 6394 6395 c.moveToFirst(); 6396 assertEquals(StatusUpdates.INVISIBLE, c.getInt(0)); 6397 assertEquals("Red", c.getString(1)); 6398 c.close(); 6399 } 6400 updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail, String ringtone)6401 private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail, 6402 String ringtone) { 6403 ContentValues values = new ContentValues(); 6404 values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail); 6405 if (ringtone != null) { 6406 values.put(Contacts.CUSTOM_RINGTONE, ringtone); 6407 } 6408 6409 final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 6410 int count = mResolver.update(uri, values, null, null); 6411 assertEquals(1, count); 6412 } 6413 updateSendToVoicemailAndRingtoneWithSelection(long contactId, boolean sendToVoicemail, String ringtone)6414 private void updateSendToVoicemailAndRingtoneWithSelection(long contactId, 6415 boolean sendToVoicemail, String ringtone) { 6416 ContentValues values = new ContentValues(); 6417 values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail); 6418 if (ringtone != null) { 6419 values.put(Contacts.CUSTOM_RINGTONE, ringtone); 6420 } 6421 6422 int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId, 6423 null); 6424 assertEquals(1, count); 6425 } 6426 assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail, String expectedRingtone)6427 private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail, 6428 String expectedRingtone) { 6429 Cursor c = queryContact(contactId); 6430 assertTrue(c.moveToNext()); 6431 int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL)); 6432 assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail); 6433 String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE)); 6434 if (expectedRingtone == null) { 6435 assertNull(ringtone); 6436 } else { 6437 assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone)); 6438 } 6439 c.close(); 6440 } 6441 6442 @Test testContactVisibilityUpdateOnMembershipChange()6443 public void testContactVisibilityUpdateOnMembershipChange() { 6444 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 6445 assertVisibility(rawContactId, "0"); 6446 6447 long visibleGroupId = createGroup(mAccount, "123", "Visible", 1); 6448 long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0); 6449 6450 Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId); 6451 assertVisibility(rawContactId, "1"); 6452 6453 Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId); 6454 assertVisibility(rawContactId, "1"); 6455 6456 mResolver.delete(membership1, null, null); 6457 assertVisibility(rawContactId, "0"); 6458 6459 ContentValues values = new ContentValues(); 6460 values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId); 6461 6462 mResolver.update(membership2, values, null, null); 6463 assertVisibility(rawContactId, "1"); 6464 } 6465 assertVisibility(long rawContactId, String expectedValue)6466 private void assertVisibility(long rawContactId, String expectedValue) { 6467 assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId), 6468 null, Contacts.IN_VISIBLE_GROUP, expectedValue); 6469 } 6470 6471 @Test testSupplyingBothValuesAndParameters()6472 public void testSupplyingBothValuesAndParameters() throws Exception { 6473 Account account = new Account("account 1", "type%/:1"); 6474 Uri uri = ContactsContract.Groups.CONTENT_URI.buildUpon() 6475 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_NAME, account.name) 6476 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_TYPE, account.type) 6477 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") 6478 .build(); 6479 6480 ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri); 6481 builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type); 6482 builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name); 6483 builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some id"); 6484 builder.withValue(ContactsContract.Groups.TITLE, "some name"); 6485 builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1); 6486 6487 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build())); 6488 6489 builder = ContentProviderOperation.newInsert(uri); 6490 builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type + "diff"); 6491 builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name); 6492 builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some other id"); 6493 builder.withValue(ContactsContract.Groups.TITLE, "some other name"); 6494 builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1); 6495 6496 try { 6497 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build())); 6498 fail("Expected IllegalArgumentException"); 6499 } catch (IllegalArgumentException ex) { 6500 // Expected 6501 } 6502 } 6503 6504 @Test testContentEntityIterator()6505 public void testContentEntityIterator() { 6506 // create multiple contacts and check that the selected ones are returned 6507 long id; 6508 6509 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 6510 long groupId2 = createGroup(mAccount, "gsid2", "title2"); 6511 6512 id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID, "c0"); 6513 insertGroupMembership(id, "gsid1"); 6514 insertEmail(id, "[email protected]"); 6515 insertPhoneNumber(id, "5551212c0"); 6516 6517 long c1 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID, 6518 "c1"); 6519 Uri id_1_0 = insertGroupMembership(id, "gsid1"); 6520 Uri id_1_1 = insertGroupMembership(id, "gsid2"); 6521 Uri id_1_2 = insertEmail(id, "[email protected]"); 6522 Uri id_1_3 = insertPhoneNumber(id, "5551212c1"); 6523 6524 long c2 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID, 6525 "c2"); 6526 Uri id_2_0 = insertGroupMembership(id, "gsid1"); 6527 Uri id_2_1 = insertEmail(id, "[email protected]"); 6528 Uri id_2_2 = insertPhoneNumber(id, "5551212c2"); 6529 6530 long c3 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID, 6531 "c3"); 6532 Uri id_3_0 = insertGroupMembership(id, groupId2); 6533 Uri id_3_1 = insertEmail(id, "[email protected]"); 6534 Uri id_3_2 = insertPhoneNumber(id, "5551212c3"); 6535 6536 EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query( 6537 TestUtil.maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), 6538 null, RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null)); 6539 Entity entity; 6540 ContentValues[] subValues; 6541 entity = iterator.next(); 6542 assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 6543 subValues = asSortedContentValuesArray(entity.getSubValues()); 6544 assertEquals(4, subValues.length); 6545 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 6546 Data._ID, id_1_0, 6547 GroupMembership.GROUP_ROW_ID, groupId1, 6548 GroupMembership.GROUP_SOURCE_ID, "gsid1"); 6549 assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE, 6550 Data._ID, id_1_1, 6551 GroupMembership.GROUP_ROW_ID, groupId2, 6552 GroupMembership.GROUP_SOURCE_ID, "gsid2"); 6553 assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE, 6554 Data._ID, id_1_2, 6555 Email.DATA, "[email protected]"); 6556 assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE, 6557 Data._ID, id_1_3, 6558 Email.DATA, "5551212c1"); 6559 6560 entity = iterator.next(); 6561 assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 6562 subValues = asSortedContentValuesArray(entity.getSubValues()); 6563 assertEquals(3, subValues.length); 6564 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 6565 Data._ID, id_2_0, 6566 GroupMembership.GROUP_ROW_ID, groupId1, 6567 GroupMembership.GROUP_SOURCE_ID, "gsid1"); 6568 assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE, 6569 Data._ID, id_2_1, 6570 Email.DATA, "[email protected]"); 6571 assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE, 6572 Data._ID, id_2_2, 6573 Email.DATA, "5551212c2"); 6574 6575 entity = iterator.next(); 6576 assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 6577 subValues = asSortedContentValuesArray(entity.getSubValues()); 6578 assertEquals(3, subValues.length); 6579 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 6580 Data._ID, id_3_0, 6581 GroupMembership.GROUP_ROW_ID, groupId2, 6582 GroupMembership.GROUP_SOURCE_ID, "gsid2"); 6583 assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE, 6584 Data._ID, id_3_1, 6585 Email.DATA, "[email protected]"); 6586 assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE, 6587 Data._ID, id_3_2, 6588 Email.DATA, "5551212c3"); 6589 6590 assertFalse(iterator.hasNext()); 6591 iterator.close(); 6592 } 6593 6594 @Test testDataCreateUpdateDeleteByMimeType()6595 public void testDataCreateUpdateDeleteByMimeType() throws Exception { 6596 long rawContactId = RawContactUtil.createRawContact(mResolver); 6597 6598 ContentValues values = new ContentValues(); 6599 values.put(Data.RAW_CONTACT_ID, rawContactId); 6600 values.put(Data.MIMETYPE, "testmimetype"); 6601 values.put(Data.RES_PACKAGE, "oldpackage"); 6602 values.put(Data.IS_PRIMARY, 1); 6603 values.put(Data.IS_SUPER_PRIMARY, 1); 6604 values.put(Data.DATA1, "old1"); 6605 values.put(Data.DATA2, "old2"); 6606 values.put(Data.DATA3, "old3"); 6607 values.put(Data.DATA4, "old4"); 6608 values.put(Data.DATA5, "old5"); 6609 values.put(Data.DATA6, "old6"); 6610 values.put(Data.DATA7, "old7"); 6611 values.put(Data.DATA8, "old8"); 6612 values.put(Data.DATA9, "old9"); 6613 values.put(Data.DATA10, "old10"); 6614 values.put(Data.DATA11, "old11"); 6615 values.put(Data.DATA12, "old12"); 6616 values.put(Data.DATA13, "old13"); 6617 values.put(Data.DATA14, "old14"); 6618 values.put(Data.DATA15, "old15"); 6619 values.put(Data.CARRIER_PRESENCE, 0); 6620 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "oldcomponentname"); 6621 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "oldid"); 6622 Uri uri = mResolver.insert(Data.CONTENT_URI, values); 6623 assertStoredValues(uri, values); 6624 assertNetworkNotified(true); 6625 6626 values.clear(); 6627 values.put(Data.RES_PACKAGE, "newpackage"); 6628 values.put(Data.IS_PRIMARY, 0); 6629 values.put(Data.IS_SUPER_PRIMARY, 0); 6630 values.put(Data.DATA1, "new1"); 6631 values.put(Data.DATA2, "new2"); 6632 values.put(Data.DATA3, "new3"); 6633 values.put(Data.DATA4, "new4"); 6634 values.put(Data.DATA5, "new5"); 6635 values.put(Data.DATA6, "new6"); 6636 values.put(Data.DATA7, "new7"); 6637 values.put(Data.DATA8, "new8"); 6638 values.put(Data.DATA9, "new9"); 6639 values.put(Data.DATA10, "new10"); 6640 values.put(Data.DATA11, "new11"); 6641 values.put(Data.DATA12, "new12"); 6642 values.put(Data.DATA13, "new13"); 6643 values.put(Data.DATA14, "new14"); 6644 values.put(Data.DATA15, "new15"); 6645 values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE); 6646 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "newcomponentname"); 6647 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "newid"); 6648 mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId + 6649 " AND " + Data.MIMETYPE + "='testmimetype'", null); 6650 assertNetworkNotified(true); 6651 6652 assertStoredValues(uri, values); 6653 6654 int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId 6655 + " AND " + Data.MIMETYPE + "='testmimetype'", null); 6656 assertEquals(1, count); 6657 assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId 6658 + " AND " + Data.MIMETYPE + "='testmimetype'", null)); 6659 assertNetworkNotified(true); 6660 } 6661 6662 @Test testRawContactQuery()6663 public void testRawContactQuery() { 6664 Account account1 = new Account("a", "b"); 6665 Account account2 = new Account("c", "d"); 6666 long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1); 6667 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 6668 6669 Uri uri1 = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1); 6670 Uri uri2 = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2); 6671 assertEquals(1, getCount(uri1, null, null)); 6672 assertEquals(1, getCount(uri2, null, null)); 6673 assertStoredValue(uri1, RawContacts._ID, rawContactId1) ; 6674 assertStoredValue(uri2, RawContacts._ID, rawContactId2) ; 6675 6676 Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1); 6677 Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2); 6678 assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ; 6679 assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ; 6680 } 6681 6682 @Test testRawContactDeletion()6683 public void testRawContactDeletion() { 6684 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 6685 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 6686 6687 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]"); 6688 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 6689 StatusUpdates.AVAILABLE, null, 6690 StatusUpdates.CAPABILITY_HAS_CAMERA); 6691 long contactId = queryContactId(rawContactId); 6692 6693 assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 6694 null, null)); 6695 assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 6696 + rawContactId, null)); 6697 6698 mResolver.delete(uri, null, null); 6699 6700 assertStoredValue(uri, RawContacts.DELETED, "1"); 6701 assertNetworkNotified(true); 6702 6703 Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount); 6704 mResolver.delete(permanentDeletionUri, null, null); 6705 assertEquals(0, getCount(uri, null, null)); 6706 assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 6707 null, null)); 6708 assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 6709 + rawContactId, null)); 6710 assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null)); 6711 assertNetworkNotified(false); 6712 } 6713 6714 @Test testRawContactDeletionKeepingAggregateContact()6715 public void testRawContactDeletionKeepingAggregateContact() { 6716 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, mAccount); 6717 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, mAccount); 6718 setAggregationException( 6719 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 6720 6721 long contactId = queryContactId(rawContactId1); 6722 6723 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 6724 Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount); 6725 mResolver.delete(permanentDeletionUri, null, null); 6726 assertEquals(0, getCount(uri, null, null)); 6727 assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null)); 6728 } 6729 6730 @Test testRawContactDeletion_byAccountParam()6731 public void testRawContactDeletion_byAccountParam() { 6732 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 6733 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 6734 6735 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]"); 6736 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 6737 StatusUpdates.AVAILABLE, null, 6738 StatusUpdates.CAPABILITY_HAS_CAMERA); 6739 assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 6740 null, null)); 6741 assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 6742 + rawContactId, null)); 6743 6744 // Do not delete if we are deleting with wrong account. 6745 Uri deleteWithWrongAccountUri = 6746 RawContacts.CONTENT_URI.buildUpon() 6747 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name) 6748 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type) 6749 .build(); 6750 int numDeleted = mResolver.delete(deleteWithWrongAccountUri, null, null); 6751 assertEquals(0, numDeleted); 6752 6753 assertStoredValue(uri, RawContacts.DELETED, "0"); 6754 6755 // Delete if we are deleting with correct account. 6756 Uri deleteWithCorrectAccountUri = 6757 RawContacts.CONTENT_URI.buildUpon() 6758 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name) 6759 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type) 6760 .build(); 6761 numDeleted = mResolver.delete(deleteWithCorrectAccountUri, null, null); 6762 assertEquals(1, numDeleted); 6763 6764 assertStoredValue(uri, RawContacts.DELETED, "1"); 6765 } 6766 6767 @Test testRawContactDeletion_byAccountSelection()6768 public void testRawContactDeletion_byAccountSelection() { 6769 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 6770 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 6771 6772 // Do not delete if we are deleting with wrong account. 6773 int numDeleted = mResolver.delete(RawContacts.CONTENT_URI, 6774 RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?", 6775 new String[] {mAccountTwo.name, mAccountTwo.type}); 6776 assertEquals(0, numDeleted); 6777 6778 assertStoredValue(uri, RawContacts.DELETED, "0"); 6779 6780 // Delete if we are deleting with correct account. 6781 numDeleted = mResolver.delete(RawContacts.CONTENT_URI, 6782 RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?", 6783 new String[] {mAccount.name, mAccount.type}); 6784 assertEquals(1, numDeleted); 6785 6786 assertStoredValue(uri, RawContacts.DELETED, "1"); 6787 } 6788 6789 /** 6790 * Test for {@link ContactsProvider2#stringToAccounts} and 6791 * {@link ContactsProvider2#accountsToString}. 6792 */ 6793 @Test testAccountsToString()6794 public void testAccountsToString() { 6795 final Set<Account> EXPECTED_0 = Sets.newHashSet(); 6796 final Set<Account> EXPECTED_1 = Sets.newHashSet(TestUtil.ACCOUNT_1); 6797 final Set<Account> EXPECTED_2 = Sets.newHashSet(TestUtil.ACCOUNT_2); 6798 final Set<Account> EXPECTED_1_2 = Sets.newHashSet(TestUtil.ACCOUNT_1, TestUtil.ACCOUNT_2); 6799 6800 final Set<Account> ACTUAL_0 = Sets.newHashSet(); 6801 final Set<Account> ACTUAL_1 = Sets.newHashSet(TestUtil.ACCOUNT_1); 6802 final Set<Account> ACTUAL_2 = Sets.newHashSet(TestUtil.ACCOUNT_2); 6803 final Set<Account> ACTUAL_1_2 = Sets.newHashSet(TestUtil.ACCOUNT_2, TestUtil.ACCOUNT_1); 6804 6805 assertTrue(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_0))); 6806 assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1))); 6807 assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_2))); 6808 assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1_2))); 6809 6810 assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_0))); 6811 assertTrue(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1))); 6812 assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_2))); 6813 assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1_2))); 6814 6815 assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_0))); 6816 assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1))); 6817 assertTrue(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_2))); 6818 assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1_2))); 6819 6820 assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_0))); 6821 assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1))); 6822 assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_2))); 6823 assertTrue(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1_2))); 6824 6825 try { 6826 ContactsProvider2.stringToAccounts("x"); 6827 fail("Didn't throw for malformed input"); 6828 } catch (IllegalArgumentException expected) { 6829 } 6830 } 6831 accountsToStringToAccounts(Set<Account> accounts)6832 private static final Set<Account> accountsToStringToAccounts(Set<Account> accounts) { 6833 return ContactsProvider2.stringToAccounts(ContactsProvider2.accountsToString(accounts)); 6834 } 6835 6836 /** 6837 * Test for {@link ContactsProvider2#haveAccountsChanged} and 6838 * {@link ContactsProvider2#saveAccounts}. 6839 */ 6840 @Test testHaveAccountsChanged()6841 public void testHaveAccountsChanged() { 6842 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 6843 6844 final Account[] ACCOUNTS_0 = new Account[] {}; 6845 final Account[] ACCOUNTS_1 = new Account[] {TestUtil.ACCOUNT_1}; 6846 final Account[] ACCOUNTS_2 = new Account[] {TestUtil.ACCOUNT_2}; 6847 final Account[] ACCOUNTS_1_2 = new Account[] {TestUtil.ACCOUNT_1, TestUtil.ACCOUNT_2}; 6848 final Account[] ACCOUNTS_2_1 = new Account[] {TestUtil.ACCOUNT_2, TestUtil.ACCOUNT_1}; 6849 6850 // Add ACCOUNT_1 6851 6852 assertTrue(cp.haveAccountsChanged(ACCOUNTS_1)); 6853 cp.saveAccounts(ACCOUNTS_1); 6854 assertFalse(cp.haveAccountsChanged(ACCOUNTS_1)); 6855 6856 // Add ACCOUNT_2 6857 6858 assertTrue(cp.haveAccountsChanged(ACCOUNTS_1_2)); 6859 // (try with reverse order) 6860 assertTrue(cp.haveAccountsChanged(ACCOUNTS_2_1)); 6861 cp.saveAccounts(ACCOUNTS_1_2); 6862 assertFalse(cp.haveAccountsChanged(ACCOUNTS_1_2)); 6863 // (try with reverse order) 6864 assertFalse(cp.haveAccountsChanged(ACCOUNTS_2_1)); 6865 6866 // Remove ACCOUNT_1 6867 6868 assertTrue(cp.haveAccountsChanged(ACCOUNTS_2)); 6869 cp.saveAccounts(ACCOUNTS_2); 6870 assertFalse(cp.haveAccountsChanged(ACCOUNTS_2)); 6871 6872 // Remove ACCOUNT_2 6873 6874 assertTrue(cp.haveAccountsChanged(ACCOUNTS_0)); 6875 cp.saveAccounts(ACCOUNTS_0); 6876 assertFalse(cp.haveAccountsChanged(ACCOUNTS_0)); 6877 6878 // Test with malformed DB property. 6879 6880 final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest(); 6881 dbHelper.setProperty(DbProperties.KNOWN_ACCOUNTS, "x"); 6882 6883 // With malformed property the method always return true. 6884 assertTrue(cp.haveAccountsChanged(ACCOUNTS_0)); 6885 assertTrue(cp.haveAccountsChanged(ACCOUNTS_1)); 6886 } 6887 6888 @Test testAccountsUpdated()6889 public void testAccountsUpdated() { 6890 // This is to ensure we do not delete contacts with null, null (account name, type) 6891 // accidentally. 6892 long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "James", "Sullivan"); 6893 insertPhoneNumber(rawContactId3, "5234567890"); 6894 Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3); 6895 assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null)); 6896 6897 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 6898 mActor.setAccounts(new Account[]{mAccount, mAccountTwo}); 6899 cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo}); 6900 assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null)); 6901 assertStoredValue( 6902 rawContact3, RawContacts.ACCOUNT_NAME, 6903 AccountWithDataSet.LOCAL.getAccountName()); 6904 assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, 6905 AccountWithDataSet.LOCAL.getAccountType()); 6906 6907 long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount); 6908 insertEmail(rawContactId1, "[email protected]"); 6909 long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 6910 insertEmail(rawContactId2, "[email protected]"); 6911 insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]"); 6912 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "[email protected]", 6913 StatusUpdates.AVAILABLE, null, 6914 StatusUpdates.CAPABILITY_HAS_CAMERA); 6915 6916 mActor.setAccounts(new Account[]{mAccount}); 6917 cp.onAccountsUpdated(new Account[]{mAccount}); 6918 assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null)); 6919 assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 6920 + rawContactId2, null)); 6921 } 6922 6923 @Test testAccountDeletion()6924 public void testAccountDeletion() { 6925 Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE); 6926 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 6927 mActor.setAccounts(new Account[]{readOnlyAccount, mAccount}); 6928 cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount}); 6929 6930 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe", 6931 readOnlyAccount); 6932 Uri photoUri1 = insertPhoto(rawContactId1); 6933 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "john", "doe", 6934 mAccount); 6935 Uri photoUri2 = insertPhoto(rawContactId2); 6936 storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1"); 6937 6938 assertAggregated(rawContactId1, rawContactId2); 6939 6940 long contactId = queryContactId(rawContactId1); 6941 6942 // The display name should come from the writable account 6943 assertStoredValue(Uri.withAppendedPath( 6944 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 6945 Contacts.Data.CONTENT_DIRECTORY), 6946 Contacts.DISPLAY_NAME, "john doe"); 6947 6948 // The photo should be the one we marked as super-primary 6949 assertStoredValue(Contacts.CONTENT_URI, contactId, 6950 Contacts.PHOTO_ID, ContentUris.parseId(photoUri2)); 6951 6952 mActor.setAccounts(new Account[]{readOnlyAccount}); 6953 // Remove the writable account 6954 cp.onAccountsUpdated(new Account[]{readOnlyAccount}); 6955 6956 // The display name should come from the remaining account 6957 assertStoredValue(Uri.withAppendedPath( 6958 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 6959 Contacts.Data.CONTENT_DIRECTORY), 6960 Contacts.DISPLAY_NAME, "John Doe"); 6961 6962 // The photo should be the remaining one 6963 assertStoredValue(Contacts.CONTENT_URI, contactId, 6964 Contacts.PHOTO_ID, ContentUris.parseId(photoUri1)); 6965 } 6966 6967 @Test 6968 @RequiresFlagsEnabled(Flags.FLAG_CP2_SYNC_SEARCH_INDEX_FLAG) testSearchIndexUpdatedOnAccountDeletion()6969 public void testSearchIndexUpdatedOnAccountDeletion() { 6970 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 6971 SQLiteDatabase db = cp.getDatabaseHelper().getReadableDatabase(); 6972 mActor.setAccounts(new Account[]{mAccount}); 6973 cp.onAccountsUpdated(new Account[]{mAccount}); 6974 6975 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Wick", 6976 mAccount); 6977 6978 // Assert the contact is in the search index 6979 assertStoredValue(buildFilterUri("wick", false), SearchSnippets.SNIPPET, null); 6980 assertEquals(1, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 6981 6982 // Remove the account 6983 mActor.setAccounts(new Account[]{}); 6984 cp.onAccountsUpdated(new Account[]{}); 6985 6986 // Assert the contact is no longer searchable 6987 assertRowCount(0, buildFilterUri("wick", false), null, null); 6988 6989 // Assert the contact is no longer in the search index table 6990 assertEquals(0, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 6991 } 6992 6993 @Test 6994 @RequiresFlagsEnabled(Flags.FLAG_CP2_SYNC_SEARCH_INDEX_FLAG) testSearchIndexUpdatedOnAccountDeletion_withMultipleAccounts()6995 public void testSearchIndexUpdatedOnAccountDeletion_withMultipleAccounts() { 6996 Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE); 6997 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 6998 SQLiteDatabase db = cp.getDatabaseHelper().getReadableDatabase(); 6999 mActor.setAccounts(new Account[]{readOnlyAccount, mAccount}); 7000 cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount}); 7001 7002 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Wick", 7003 readOnlyAccount); 7004 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "john", "wick", 7005 mAccount); 7006 insertEmail(rawContactId2, "[email protected]", true); 7007 7008 assertAggregated(rawContactId1, rawContactId2); 7009 7010 // Assert the contact is searchable by name and email 7011 assertStoredValue(buildFilterUri("wick", false), SearchSnippets.SNIPPET, null); 7012 assertStoredValue(buildFilterUri("movie", false), SearchSnippets.SNIPPET, 7013 "person@[movie].com"); 7014 // Since contacts are aggregated only 1 entry should be present in search index 7015 assertEquals(1, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7016 7017 // Remove the writable account 7018 mActor.setAccounts(new Account[]{readOnlyAccount}); 7019 cp.onAccountsUpdated(new Account[]{readOnlyAccount}); 7020 7021 // Assert the contact is searchable by name but not by email 7022 assertStoredValue(buildFilterUri("wick", false), SearchSnippets.SNIPPET, null); 7023 assertRowCount(0, buildFilterUri("movie", false), null, null); 7024 7025 // Assert the contact is still in the search index table 7026 assertEquals(1, DatabaseUtils.longForQuery(db, "SELECT count(*) FROM search_index", null)); 7027 } 7028 7029 @Test 7030 @RequiresFlagsEnabled(Flags.FLAG_CP2_SYNC_SEARCH_INDEX_FLAG) testSearchIndexUpdatedOnAccountDeletion_withMaxStaleContacts()7031 public void testSearchIndexUpdatedOnAccountDeletion_withMaxStaleContacts() { 7032 Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE); 7033 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 7034 SQLiteDatabase db = cp.getDatabaseHelper().getReadableDatabase(); 7035 mActor.setAccounts(new Account[]{readOnlyAccount, mAccount}); 7036 cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount}); 7037 7038 // The maximum amount of stale contacts before rebuilding search index completely 7039 cp.setSearchIndexMaxUpdateFilterContacts(5); 7040 7041 // Add more contacts than the max amount of stale contacts, such that we trigger a 7042 // rebuild of the search index during the account removal process 7043 for (int i = 0; i < 10; i++) { 7044 String firstName = "first" + i; 7045 String lastName = "last" + i; 7046 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, firstName, 7047 lastName, readOnlyAccount); 7048 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, firstName, 7049 lastName, mAccount); 7050 insertEmail(rawContactId2, "person@corp" + i + ".com", true); 7051 7052 assertAggregated(rawContactId1, rawContactId2); 7053 7054 // Assert the contact is searchable by name and email 7055 assertStoredValue(buildFilterUri(firstName, false), SearchSnippets.SNIPPET, null); 7056 assertStoredValue(buildFilterUri("corp" + i, false), SearchSnippets.SNIPPET, 7057 "person@[corp" + i + "].com"); 7058 // Since contacts are aggregated only 1 entry should be present in search index 7059 assertEquals(i + 1, 7060 DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7061 } 7062 7063 // Remove the writable account 7064 mActor.setAccounts(new Account[]{readOnlyAccount}); 7065 cp.onAccountsUpdated(new Account[]{readOnlyAccount}); 7066 7067 for (int i = 0; i < 10; i++) { 7068 String firstName = "first" + i; 7069 String lastName = "last" + i; 7070 // Assert the contact is searchable by name but not by email 7071 assertStoredValue(buildFilterUri(firstName, false), SearchSnippets.SNIPPET, null); 7072 assertRowCount(0, buildFilterUri("corp" + i, false), null, null); 7073 } 7074 7075 // Assert all of the contacts are still in the search index table 7076 assertEquals(10, DatabaseUtils.longForQuery(db, "SELECT count(*) FROM search_index", null)); 7077 } 7078 7079 @Test 7080 @RequiresFlagsEnabled(Flags.FLAG_CP2_SYNC_SEARCH_INDEX_FLAG) testSearchIndexUpdatedOnRawContactOperations()7081 public void testSearchIndexUpdatedOnRawContactOperations() { 7082 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 7083 SQLiteDatabase db = cp.getDatabaseHelper().getReadableDatabase(); 7084 7085 assertEquals(0, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7086 7087 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Wick"); 7088 Uri emailUri = insertEmail(rawContactId, "[email protected]"); 7089 7090 // Assert contact is in the search index 7091 assertStoredValue(buildFilterUri("wick", false), SearchSnippets.SNIPPET, null); 7092 assertStoredValue(buildFilterUri("movie", false), SearchSnippets.SNIPPET, 7093 "john@[movie].com"); 7094 assertEquals(1, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7095 assertEquals(0, DatabaseUtils.longForQuery(db, 7096 "SELECT COUNT(*) FROM stale_search_index_contacts", null)); 7097 7098 // Update raw contact with email 7099 ContentValues values = new ContentValues(); 7100 values.put(Data.RAW_CONTACT_ID, rawContactId); 7101 values.put(Email.DATA, "[email protected]"); 7102 mResolver.update(emailUri, values, null); 7103 7104 // Assert contact is updated in the search index 7105 assertStoredValue(buildFilterUri("wick", false), SearchSnippets.SNIPPET, null); 7106 assertRowCount(0, buildFilterUri("movie", false), null, null); 7107 assertStoredValue(buildFilterUri("continental", false), SearchSnippets.SNIPPET, 7108 "john@[continental].com"); 7109 assertEquals(1, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7110 assertEquals(0, DatabaseUtils.longForQuery(db, 7111 "SELECT COUNT(*) FROM stale_search_index_contacts", null)); 7112 7113 // Delete the raw contact 7114 RawContactUtil.delete(mResolver, rawContactId, true); 7115 7116 // Assert the contact is no longer searchable 7117 assertRowCount(0, buildFilterUri("wick", false), null, null); 7118 assertEquals(0, DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM search_index", null)); 7119 assertEquals(0, DatabaseUtils.longForQuery(db, 7120 "SELECT COUNT(*) FROM stale_search_index_contacts", null)); 7121 } 7122 7123 @Test testStreamItemsCleanedUpOnAccountRemoval()7124 public void testStreamItemsCleanedUpOnAccountRemoval() { 7125 Account doomedAccount = new Account("doom", "doom"); 7126 Account safeAccount = mAccount; 7127 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 7128 mActor.setAccounts(new Account[]{doomedAccount, safeAccount}); 7129 cp.onAccountsUpdated(new Account[]{doomedAccount, safeAccount}); 7130 7131 // Create a doomed raw contact, stream item, and photo. 7132 long doomedRawContactId = RawContactUtil.createRawContactWithName(mResolver, doomedAccount); 7133 Uri doomedStreamItemUri = 7134 insertStreamItem(doomedRawContactId, buildGenericStreamItemValues(), doomedAccount); 7135 long doomedStreamItemId = ContentUris.parseId(doomedStreamItemUri); 7136 Uri doomedStreamItemPhotoUri = insertStreamItemPhoto( 7137 doomedStreamItemId, buildGenericStreamItemPhotoValues(0), doomedAccount); 7138 7139 // Create a safe raw contact, stream item, and photo. 7140 long safeRawContactId = RawContactUtil.createRawContactWithName(mResolver, safeAccount); 7141 Uri safeStreamItemUri = 7142 insertStreamItem(safeRawContactId, buildGenericStreamItemValues(), safeAccount); 7143 long safeStreamItemId = ContentUris.parseId(safeStreamItemUri); 7144 Uri safeStreamItemPhotoUri = insertStreamItemPhoto( 7145 safeStreamItemId, buildGenericStreamItemPhotoValues(0), safeAccount); 7146 long safeStreamItemPhotoId = ContentUris.parseId(safeStreamItemPhotoUri); 7147 7148 // Remove the doomed account. 7149 mActor.setAccounts(new Account[]{safeAccount}); 7150 cp.onAccountsUpdated(new Account[]{safeAccount}); 7151 7152 // Check that the doomed stuff has all been nuked. 7153 ContentValues[] noValues = new ContentValues[0]; 7154 assertStoredValues(ContentUris.withAppendedId(RawContacts.CONTENT_URI, doomedRawContactId), 7155 noValues); 7156 assertStoredValues(doomedStreamItemUri, noValues); 7157 assertStoredValues(doomedStreamItemPhotoUri, noValues); 7158 7159 // Check that the safe stuff lives on. 7160 assertStoredValue(RawContacts.CONTENT_URI, safeRawContactId, RawContacts._ID, 7161 safeRawContactId); 7162 assertStoredValue(safeStreamItemUri, StreamItems._ID, safeStreamItemId); 7163 assertStoredValue(safeStreamItemPhotoUri, StreamItemPhotos._ID, safeStreamItemPhotoId); 7164 } 7165 7166 @Test testContactDeletion()7167 public void testContactDeletion() { 7168 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe", 7169 TestUtil.ACCOUNT_1); 7170 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe", 7171 TestUtil.ACCOUNT_2); 7172 7173 long contactId = queryContactId(rawContactId1); 7174 7175 mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null); 7176 7177 assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1), 7178 RawContacts.DELETED, "1"); 7179 assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2), 7180 RawContacts.DELETED, "1"); 7181 } 7182 7183 @Test testMarkAsDirtyParameter()7184 public void testMarkAsDirtyParameter() { 7185 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7186 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 7187 7188 Uri uri = DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 7189 clearDirty(rawContactUri); 7190 Uri updateUri = setCallerIsSyncAdapter(uri, mAccount); 7191 7192 ContentValues values = new ContentValues(); 7193 values.put(StructuredName.FAMILY_NAME, "Dough"); 7194 mResolver.update(updateUri, values, null, null); 7195 assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough"); 7196 assertDirty(rawContactUri, false); 7197 assertNetworkNotified(false); 7198 } 7199 7200 @Test testDirtyWhenRawContactInsert()7201 public void testDirtyWhenRawContactInsert() { 7202 // When inserting a rawcontact. 7203 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7204 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 7205 assertDirty(rawContactUri, false); 7206 assertMetadataDirty(rawContactUri, false); 7207 assertNetworkNotified(true); 7208 } 7209 7210 @Test testRawContactDirtyAndVersion()7211 public void testRawContactDirtyAndVersion() { 7212 final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7213 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId); 7214 assertDirty(uri, false); 7215 long version = getVersion(uri); 7216 7217 ContentValues values = new ContentValues(); 7218 values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1); 7219 values.put(ContactsContract.RawContacts.AGGREGATION_MODE, 7220 RawContacts.AGGREGATION_MODE_IMMEDIATE); 7221 assertEquals(1, mResolver.update(uri, values, null, null)); 7222 assertEquals(version, getVersion(uri)); 7223 7224 assertDirty(uri, false); 7225 assertMetadataDirty(uri, false); 7226 assertNetworkNotified(false); 7227 7228 Uri emailUri = insertEmail(rawContactId, "[email protected]"); 7229 assertDirty(uri, true); 7230 assertNetworkNotified(true); 7231 ++version; 7232 assertEquals(version, getVersion(uri)); 7233 clearDirty(uri); 7234 7235 values = new ContentValues(); 7236 values.put(Email.DATA, "[email protected]"); 7237 mResolver.update(emailUri, values, null, null); 7238 assertDirty(uri, true); 7239 assertNetworkNotified(true); 7240 ++version; 7241 assertEquals(version, getVersion(uri)); 7242 clearDirty(uri); 7243 7244 mResolver.delete(emailUri, null, null); 7245 assertDirty(uri, true); 7246 assertNetworkNotified(true); 7247 ++version; 7248 assertEquals(version, getVersion(uri)); 7249 } 7250 7251 @Test testRawContactClearDirty()7252 public void testRawContactClearDirty() { 7253 final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7254 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, 7255 rawContactId); 7256 long version = getVersion(uri); 7257 insertEmail(rawContactId, "[email protected]"); 7258 assertDirty(uri, true); 7259 version++; 7260 assertEquals(version, getVersion(uri)); 7261 7262 clearDirty(uri); 7263 assertDirty(uri, false); 7264 assertEquals(version, getVersion(uri)); 7265 } 7266 7267 @Test testRawContactDeletionSetsDirty()7268 public void testRawContactDeletionSetsDirty() { 7269 final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7270 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, 7271 rawContactId); 7272 long version = getVersion(uri); 7273 clearDirty(uri); 7274 assertDirty(uri, false); 7275 7276 mResolver.delete(uri, null, null); 7277 assertStoredValue(uri, RawContacts.DELETED, "1"); 7278 assertDirty(uri, true); 7279 assertNetworkNotified(true); 7280 version++; 7281 assertEquals(version, getVersion(uri)); 7282 } 7283 7284 @Test testNotifyMetadataChangeForRawContactInsertBySyncAdapter()7285 public void testNotifyMetadataChangeForRawContactInsertBySyncAdapter() { 7286 Uri uri = RawContacts.CONTENT_URI.buildUpon() 7287 .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.name) 7288 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccount.type) 7289 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, true + "") 7290 .build(); 7291 7292 long rawContactId = ContentUris.parseId(mResolver.insert(uri, new ContentValues())); 7293 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 7294 assertMetadataDirty(rawContactUri, false); 7295 } 7296 7297 @Test testMarkAsMetadataDirtyForRawContactMetadataChange()7298 public void testMarkAsMetadataDirtyForRawContactMetadataChange() { 7299 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7300 long contactId = queryContactId(rawContactId); 7301 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7302 7303 ContentValues values = new ContentValues(); 7304 values.put(Contacts.STARRED, 1); 7305 mResolver.update(contactUri, values, null, null); 7306 assertStoredValue(contactUri, Contacts.STARRED, 1); 7307 7308 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 7309 assertMetadataDirty(rawContactUri, false); 7310 7311 clearMetadataDirty(rawContactUri); 7312 values = new ContentValues(); 7313 values.put(Contacts.PINNED, 1); 7314 mResolver.update(contactUri, values, null, null); 7315 assertStoredValue(contactUri, Contacts.PINNED, 1); 7316 7317 assertMetadataDirty(rawContactUri, false); 7318 7319 clearMetadataDirty(rawContactUri); 7320 values = new ContentValues(); 7321 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 7322 mResolver.update(contactUri, values, null, null); 7323 assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 1); 7324 7325 assertMetadataDirty(rawContactUri, false); 7326 } 7327 7328 @Test testMarkAsMetadataDirtyForRawContactBackupIdChange()7329 public void testMarkAsMetadataDirtyForRawContactBackupIdChange() { 7330 long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount); 7331 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 7332 7333 // Make a metadata change to set metadata_dirty. 7334 ContentValues values = new ContentValues(); 7335 values.put(RawContacts.SEND_TO_VOICEMAIL, "1"); 7336 mResolver.update(rawContactUri, values, null, null); 7337 assertMetadataDirty(rawContactUri, false); 7338 7339 // Update the backup_id and check metadata network should be notified. 7340 values = new ContentValues(); 7341 values.put(RawContacts.BACKUP_ID, "newBackupId"); 7342 mResolver.update(rawContactUri, values, null, null); 7343 assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, "newBackupId"); 7344 assertMetadataDirty(rawContactUri, false); 7345 } 7346 7347 @Test testMarkAsMetadataDirtyForAggregationExceptionChange()7348 public void testMarkAsMetadataDirtyForAggregationExceptionChange() { 7349 long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 7350 long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b")); 7351 7352 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 7353 rawContactId1, rawContactId2); 7354 7355 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1), 7356 false); 7357 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2), 7358 false); 7359 } 7360 7361 @Test testMarkAsMetadataNotDirtyForUsageStatsChange()7362 public void testMarkAsMetadataNotDirtyForUsageStatsChange() { 7363 final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a"); 7364 final long did1a = ContentUris.parseId(insertEmail(rid1, "[email protected]")); 7365 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a); 7366 7367 // Usage feedback no longer works, so "false". 7368 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rid1), false); 7369 } 7370 7371 @Test testMarkAsMetadataDirtyForDataPrimarySettingInsert()7372 public void testMarkAsMetadataDirtyForDataPrimarySettingInsert() { 7373 long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 7374 Uri mailUri11 = insertEmail(rawContactId1, "[email protected]", true, true); 7375 7376 assertStoredValue(mailUri11, Data.IS_PRIMARY, 1); 7377 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1); 7378 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1), 7379 false); 7380 } 7381 7382 @Test testMarkAsMetadataDirtyForDataPrimarySettingUpdate()7383 public void testMarkAsMetadataDirtyForDataPrimarySettingUpdate() { 7384 long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 7385 Uri mailUri1 = insertEmail(rawContactId, "[email protected]"); 7386 7387 assertStoredValue(mailUri1, Data.IS_PRIMARY, 0); 7388 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0); 7389 7390 ContentValues values = new ContentValues(); 7391 values.put(Data.IS_SUPER_PRIMARY, 1); 7392 mResolver.update(mailUri1, values, null, null); 7393 7394 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 7395 false); 7396 } 7397 7398 @Test testMarkAsMetadataDirtyForDataDelete()7399 public void testMarkAsMetadataDirtyForDataDelete() { 7400 long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 7401 Uri mailUri1 = insertEmail(rawContactId, "[email protected]", true, true); 7402 7403 mResolver.delete(mailUri1, null, null); 7404 7405 assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 7406 false); 7407 } 7408 7409 @Test testDeleteContactWithoutName()7410 public void testDeleteContactWithoutName() { 7411 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 7412 long rawContactId = ContentUris.parseId(rawContactUri); 7413 7414 Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true); 7415 7416 long contactId = queryContactId(rawContactId); 7417 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7418 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 7419 7420 int numDeleted = mResolver.delete(lookupUri, null, null); 7421 assertEquals(1, numDeleted); 7422 } 7423 7424 @Test testDeleteContactWithoutAnyData()7425 public void testDeleteContactWithoutAnyData() { 7426 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 7427 long rawContactId = ContentUris.parseId(rawContactUri); 7428 7429 long contactId = queryContactId(rawContactId); 7430 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7431 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 7432 7433 int numDeleted = mResolver.delete(lookupUri, null, null); 7434 assertEquals(1, numDeleted); 7435 } 7436 7437 @Test testDeleteContactWithEscapedUri()7438 public void testDeleteContactWithEscapedUri() { 7439 ContentValues values = new ContentValues(); 7440 values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~"); 7441 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 7442 long rawContactId = ContentUris.parseId(rawContactUri); 7443 7444 long contactId = queryContactId(rawContactId); 7445 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7446 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 7447 assertEquals(1, mResolver.delete(lookupUri, null, null)); 7448 } 7449 7450 @Test testDeleteContactComposedOfSingleLocalRawContact()7451 public void testDeleteContactComposedOfSingleLocalRawContact() { 7452 // Create a raw contact in the local (null) account 7453 long rawContactId = RawContactUtil.createRawContact(mResolver, null); 7454 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Smith"); 7455 7456 // Delete the contact 7457 long contactId = queryContactId(rawContactId); 7458 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7459 assertEquals(1, mResolver.delete(contactUri, null, null)); 7460 7461 // Assert that the raw contact was removed 7462 Cursor c1 = queryRawContact(rawContactId); 7463 assertEquals(0, c1.getCount()); 7464 c1.close(); 7465 7466 // Assert that the contact was removed 7467 Cursor c2 = mResolver.query(contactUri, null, null, null, ""); 7468 assertEquals(0, c2.getCount()); 7469 c2.close(); 7470 } 7471 7472 @Test testDeleteContactComposedOfTwoLocalRawContacts()7473 public void testDeleteContactComposedOfTwoLocalRawContacts() { 7474 // Create a raw contact in the local (null) account 7475 long rawContactId1 = RawContactUtil.createRawContact(mResolver, null); 7476 DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith"); 7477 7478 // Create another local raw contact with the same name 7479 long rawContactId2 = RawContactUtil.createRawContact(mResolver, null); 7480 DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith"); 7481 7482 // Join the two raw contacts explicitly 7483 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 7484 rawContactId1, rawContactId2); 7485 7486 // Check that the two raw contacts are aggregated together 7487 assertAggregated(rawContactId1, rawContactId2, "John Smith"); 7488 7489 // Delete the aggregate contact 7490 long contactId = queryContactId(rawContactId1); 7491 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7492 assertEquals(1, mResolver.delete(contactUri, null, null)); 7493 7494 // Assert that both of the local raw contacts were removed completely 7495 Cursor c1 = queryRawContact(rawContactId1); 7496 assertEquals(0, c1.getCount()); 7497 c1.close(); 7498 7499 Cursor c2 = queryRawContact(rawContactId2); 7500 assertEquals(0, c2.getCount()); 7501 c2.close(); 7502 7503 // Assert that the contact was removed 7504 Cursor c3 = queryContact(contactId); 7505 assertEquals(0, c3.getCount()); 7506 c3.close(); 7507 } 7508 7509 @Test testDeleteContactComposedOfSomeLocalRawContacts()7510 public void testDeleteContactComposedOfSomeLocalRawContacts() { 7511 // Create a raw contact in the local (null) account 7512 long rawContactId1 = RawContactUtil.createRawContact(mResolver, null); 7513 DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith"); 7514 7515 // Create another one in a non-local account with the same name 7516 long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount); 7517 DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith"); 7518 7519 // Check that the two new raw contacts are aggregated together 7520 assertAggregated(rawContactId1, rawContactId2, "John Smith"); 7521 7522 // Delete the aggregate contact 7523 long contactId = queryContactId(rawContactId1); 7524 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7525 assertEquals(1, mResolver.delete(contactUri, null, null)); 7526 7527 // Assert that the local raw contact was removed completely 7528 Cursor c1 = queryRawContact(rawContactId1); 7529 assertEquals(0, c1.getCount()); 7530 c1.close(); 7531 7532 // Assert that the non-local raw contact is still present just marked as deleted 7533 Cursor c2 = queryRawContact(rawContactId2); 7534 assertEquals(1, c2.getCount()); 7535 assertTrue(c2.moveToFirst()); 7536 assertEquals(1, c2.getInt(c2.getColumnIndex(RawContacts.DELETED))); 7537 c2.close(); 7538 7539 // Assert that the contact was removed 7540 Cursor c3 = queryContact(contactId); 7541 assertEquals(0, c3.getCount()); 7542 c3.close(); 7543 } 7544 7545 @Test testQueryContactWithEscapedUri()7546 public void testQueryContactWithEscapedUri() { 7547 ContentValues values = new ContentValues(); 7548 values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~"); 7549 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 7550 long rawContactId = ContentUris.parseId(rawContactUri); 7551 7552 long contactId = queryContactId(rawContactId); 7553 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7554 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 7555 Cursor c = mResolver.query(lookupUri, null, null, null, ""); 7556 assertEquals(1, c.getCount()); 7557 c.close(); 7558 } 7559 7560 @Test testGetPhotoUri()7561 public void testGetPhotoUri() { 7562 ContentValues values = new ContentValues(); 7563 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 7564 long rawContactId = ContentUris.parseId(rawContactUri); 7565 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 7566 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 7567 long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?", 7568 new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID); 7569 String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId) 7570 .toString(); 7571 7572 assertStoredValue( 7573 ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)), 7574 Contacts.PHOTO_URI, photoUri); 7575 } 7576 7577 @Test testGetPhotoViaLookupUri()7578 public void testGetPhotoViaLookupUri() throws IOException { 7579 long rawContactId = RawContactUtil.createRawContact(mResolver); 7580 long contactId = queryContactId(rawContactId); 7581 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7582 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 7583 String lookupKey = lookupUri.getPathSegments().get(2); 7584 insertPhoto(rawContactId, R.drawable.earth_small); 7585 byte[] thumbnail = loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL); 7586 7587 // Two forms of lookup key URIs should be valid - one with the contact ID, one without. 7588 Uri photoLookupUriWithId = Uri.withAppendedPath(lookupUri, "photo"); 7589 Uri photoLookupUriWithoutId = Contacts.CONTENT_LOOKUP_URI.buildUpon() 7590 .appendPath(lookupKey).appendPath("photo").build(); 7591 7592 // Try retrieving as a data record. 7593 ContentValues values = new ContentValues(); 7594 values.put(Photo.PHOTO, thumbnail); 7595 assertStoredValues(photoLookupUriWithId, values); 7596 assertStoredValues(photoLookupUriWithoutId, values); 7597 7598 // Try opening as an input stream. 7599 EvenMoreAsserts.assertImageRawData(getContext(), 7600 thumbnail, mResolver.openInputStream(photoLookupUriWithId)); 7601 EvenMoreAsserts.assertImageRawData(getContext(), 7602 thumbnail, mResolver.openInputStream(photoLookupUriWithoutId)); 7603 } 7604 7605 @Test testInputStreamForPhoto()7606 public void testInputStreamForPhoto() throws Exception { 7607 long rawContactId = RawContactUtil.createRawContact(mResolver); 7608 long contactId = queryContactId(rawContactId); 7609 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 7610 insertPhoto(rawContactId); 7611 Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI)); 7612 Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI)); 7613 7614 // Check the thumbnail. 7615 EvenMoreAsserts.assertImageRawData(getContext(), loadTestPhoto(PhotoSize.THUMBNAIL), 7616 mResolver.openInputStream(photoThumbnailUri)); 7617 7618 // Then check the display photo. Note because we only inserted a small photo, but not a 7619 // display photo, this returns the thumbnail image itself, which was compressed at 7620 // the thumnail compression rate, which is why we compare to 7621 // loadTestPhoto(PhotoSize.THUMBNAIL) rather than loadTestPhoto(PhotoSize.DISPLAY_PHOTO) 7622 // here. 7623 // (In other words, loadTestPhoto(PhotoSize.DISPLAY_PHOTO) returns the same photo as 7624 // loadTestPhoto(PhotoSize.THUMBNAIL), except it's compressed at a lower compression rate.) 7625 EvenMoreAsserts.assertImageRawData(getContext(), loadTestPhoto(PhotoSize.THUMBNAIL), 7626 mResolver.openInputStream(photoUri)); 7627 } 7628 7629 @Test testSuperPrimaryPhoto()7630 public void testSuperPrimaryPhoto() { 7631 long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 7632 Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal); 7633 long photoId1 = ContentUris.parseId(photoUri1); 7634 7635 long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b")); 7636 Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal); 7637 long photoId2 = ContentUris.parseId(photoUri2); 7638 7639 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 7640 rawContactId1, rawContactId2); 7641 7642 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 7643 queryContactId(rawContactId1)); 7644 7645 long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?", 7646 new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID); 7647 String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1) 7648 .toString(); 7649 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1); 7650 assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri); 7651 7652 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 7653 rawContactId1, rawContactId2); 7654 7655 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 7656 rawContactId1, rawContactId2); 7657 ContentValues values = new ContentValues(); 7658 values.put(Data.IS_SUPER_PRIMARY, 1); 7659 mResolver.update(photoUri2, values, null, null); 7660 7661 contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 7662 queryContactId(rawContactId1)); 7663 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2); 7664 7665 mResolver.update(photoUri1, values, null, null); 7666 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1); 7667 } 7668 7669 @Test testUpdatePhoto()7670 public void testUpdatePhoto() { 7671 ContentValues values = new ContentValues(); 7672 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 7673 long rawContactId = ContentUris.parseId(rawContactUri); 7674 DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe"); 7675 7676 Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 7677 queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY); 7678 7679 values.clear(); 7680 values.put(Data.RAW_CONTACT_ID, rawContactId); 7681 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 7682 values.putNull(Photo.PHOTO); 7683 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 7684 long photoId = ContentUris.parseId(dataUri); 7685 7686 assertEquals(0, getCount(twigUri, null, null)); 7687 7688 values.clear(); 7689 values.put(Photo.PHOTO, loadTestPhoto()); 7690 mResolver.update(dataUri, values, null, null); 7691 assertNetworkNotified(true); 7692 7693 long twigId = getStoredLongValue(twigUri, Data._ID); 7694 assertEquals(photoId, twigId); 7695 } 7696 7697 @Test testUpdateRawContactDataPhoto()7698 public void testUpdateRawContactDataPhoto() { 7699 // setup a contact with a null photo 7700 ContentValues values = new ContentValues(); 7701 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 7702 long rawContactId = ContentUris.parseId(rawContactUri); 7703 7704 // setup a photo 7705 values.put(Data.RAW_CONTACT_ID, rawContactId); 7706 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 7707 values.putNull(Photo.PHOTO); 7708 7709 // try to do an update before insert should return count == 0 7710 Uri dataUri = Uri.withAppendedPath( 7711 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 7712 RawContacts.Data.CONTENT_DIRECTORY); 7713 assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?", 7714 new String[] {Photo.CONTENT_ITEM_TYPE})); 7715 7716 mResolver.insert(Data.CONTENT_URI, values); 7717 7718 // save a photo to the db 7719 values.clear(); 7720 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 7721 values.put(Photo.PHOTO, loadTestPhoto()); 7722 assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?", 7723 new String[] {Photo.CONTENT_ITEM_TYPE})); 7724 7725 // verify the photo 7726 Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO}, 7727 Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null); 7728 storedPhoto.moveToFirst(); 7729 MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0)); 7730 storedPhoto.close(); 7731 } 7732 7733 @Test testOpenDisplayPhotoForContactId()7734 public void testOpenDisplayPhotoForContactId() throws IOException { 7735 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7736 long contactId = queryContactId(rawContactId); 7737 insertPhoto(rawContactId, R.drawable.earth_normal); 7738 Uri photoUri = Contacts.CONTENT_URI.buildUpon() 7739 .appendPath(String.valueOf(contactId)) 7740 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 7741 EvenMoreAsserts.assertImageRawData(getContext(), 7742 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 7743 mResolver.openInputStream(photoUri)); 7744 } 7745 7746 @Test testOpenDisplayPhotoForContactLookupKey()7747 public void testOpenDisplayPhotoForContactLookupKey() throws IOException { 7748 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7749 long contactId = queryContactId(rawContactId); 7750 String lookupKey = queryLookupKey(contactId); 7751 insertPhoto(rawContactId, R.drawable.earth_normal); 7752 Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon() 7753 .appendPath(lookupKey) 7754 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 7755 EvenMoreAsserts.assertImageRawData(getContext(), 7756 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 7757 mResolver.openInputStream(photoUri)); 7758 } 7759 7760 @Test testOpenDisplayPhotoForContactLookupKeyAndId()7761 public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException { 7762 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7763 long contactId = queryContactId(rawContactId); 7764 String lookupKey = queryLookupKey(contactId); 7765 insertPhoto(rawContactId, R.drawable.earth_normal); 7766 Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon() 7767 .appendPath(lookupKey) 7768 .appendPath(String.valueOf(contactId)) 7769 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 7770 EvenMoreAsserts.assertImageRawData(getContext(), 7771 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 7772 mResolver.openInputStream(photoUri)); 7773 } 7774 7775 @Test testOpenDisplayPhotoForRawContactId()7776 public void testOpenDisplayPhotoForRawContactId() throws IOException { 7777 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7778 insertPhoto(rawContactId, R.drawable.earth_normal); 7779 Uri photoUri = RawContacts.CONTENT_URI.buildUpon() 7780 .appendPath(String.valueOf(rawContactId)) 7781 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 7782 EvenMoreAsserts.assertImageRawData(getContext(), 7783 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 7784 mResolver.openInputStream(photoUri)); 7785 } 7786 7787 @Test testOpenDisplayPhotoByPhotoUri()7788 public void testOpenDisplayPhotoByPhotoUri() throws IOException { 7789 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7790 long contactId = queryContactId(rawContactId); 7791 insertPhoto(rawContactId, R.drawable.earth_normal); 7792 7793 // Get the photo URI out and check the content. 7794 String photoUri = getStoredValue( 7795 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7796 Contacts.PHOTO_URI); 7797 EvenMoreAsserts.assertImageRawData(getContext(), 7798 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 7799 mResolver.openInputStream(Uri.parse(photoUri))); 7800 } 7801 7802 @Test testPhotoUriForDisplayPhoto()7803 public void testPhotoUriForDisplayPhoto() { 7804 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7805 long contactId = queryContactId(rawContactId); 7806 7807 // Photo being inserted is larger than a thumbnail, so it will be stored as a file. 7808 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 7809 String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), 7810 Photo.PHOTO_FILE_ID); 7811 String photoUri = getStoredValue( 7812 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7813 Contacts.PHOTO_URI); 7814 7815 // Check that the photo URI differs from the thumbnail. 7816 String thumbnailUri = getStoredValue( 7817 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7818 Contacts.PHOTO_THUMBNAIL_URI); 7819 assertFalse(photoUri.equals(thumbnailUri)); 7820 7821 // URI should be of the form display_photo/ID 7822 assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(), 7823 photoUri); 7824 } 7825 7826 @Test testPhotoUriForThumbnailPhoto()7827 public void testPhotoUriForThumbnailPhoto() throws IOException { 7828 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7829 long contactId = queryContactId(rawContactId); 7830 7831 // Photo being inserted is a thumbnail, so it will only be stored in a BLOB. The photo URI 7832 // will fall back to the thumbnail URI. 7833 insertPhoto(rawContactId, R.drawable.earth_small); 7834 String photoUri = getStoredValue( 7835 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7836 Contacts.PHOTO_URI); 7837 7838 // Check that the photo URI is equal to the thumbnail URI. 7839 String thumbnailUri = getStoredValue( 7840 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7841 Contacts.PHOTO_THUMBNAIL_URI); 7842 assertEquals(photoUri, thumbnailUri); 7843 7844 // URI should be of the form contacts/ID/photo 7845 assertEquals(Uri.withAppendedPath( 7846 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7847 Contacts.Photo.CONTENT_DIRECTORY).toString(), 7848 photoUri); 7849 7850 // Loading the photo URI content should get the thumbnail. 7851 EvenMoreAsserts.assertImageRawData(getContext(), 7852 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL), 7853 mResolver.openInputStream(Uri.parse(photoUri))); 7854 } 7855 7856 @Test testWriteNewPhotoToAssetFile()7857 public void testWriteNewPhotoToAssetFile() throws Exception { 7858 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7859 long contactId = queryContactId(rawContactId); 7860 7861 // Load in a huge photo. 7862 final byte[] originalPhoto = loadPhotoFromResource( 7863 R.drawable.earth_huge, PhotoSize.ORIGINAL); 7864 7865 // Write it out. 7866 final Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon() 7867 .appendPath(String.valueOf(rawContactId)) 7868 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 7869 writePhotoAsync(writeablePhotoUri, originalPhoto); 7870 7871 // Check that the display photo and thumbnail have been set. 7872 String photoUri = null; 7873 for (int i = 0; i < 10 && photoUri == null; i++) { 7874 // Wait a tick for the photo processing to occur. 7875 Thread.sleep(100); 7876 photoUri = getStoredValue( 7877 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7878 Contacts.PHOTO_URI); 7879 } 7880 7881 assertFalse(TextUtils.isEmpty(photoUri)); 7882 String thumbnailUri = getStoredValue( 7883 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7884 Contacts.PHOTO_THUMBNAIL_URI); 7885 assertFalse(TextUtils.isEmpty(thumbnailUri)); 7886 assertNotSame(photoUri, thumbnailUri); 7887 7888 // Check the content of the display photo and thumbnail. 7889 EvenMoreAsserts.assertImageRawData(getContext(), 7890 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO), 7891 mResolver.openInputStream(Uri.parse(photoUri))); 7892 EvenMoreAsserts.assertImageRawData(getContext(), 7893 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL), 7894 mResolver.openInputStream(Uri.parse(thumbnailUri))); 7895 } 7896 7897 @Test testWriteUpdatedPhotoToAssetFile()7898 public void testWriteUpdatedPhotoToAssetFile() throws Exception { 7899 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 7900 long contactId = queryContactId(rawContactId); 7901 7902 // Insert a large photo first. 7903 insertPhoto(rawContactId, R.drawable.earth_large); 7904 String largeEarthPhotoUri = getStoredValue( 7905 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI); 7906 7907 // Load in a huge photo. 7908 byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL); 7909 7910 // Write it out. 7911 Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon() 7912 .appendPath(String.valueOf(rawContactId)) 7913 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 7914 writePhotoAsync(writeablePhotoUri, originalPhoto); 7915 7916 // Allow a second for processing to occur. 7917 Thread.sleep(1000); 7918 7919 // Check that the display photo URI has been modified. 7920 String hugeEarthPhotoUri = getStoredValue( 7921 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI); 7922 assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri)); 7923 7924 // Check the content of the display photo and thumbnail. 7925 String hugeEarthThumbnailUri = getStoredValue( 7926 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 7927 Contacts.PHOTO_THUMBNAIL_URI); 7928 EvenMoreAsserts.assertImageRawData(getContext(), 7929 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO), 7930 mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri))); 7931 EvenMoreAsserts.assertImageRawData(getContext(), 7932 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL), 7933 mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri))); 7934 7935 } 7936 writePhotoAsync(final Uri uri, final byte[] photoBytes)7937 private void writePhotoAsync(final Uri uri, final byte[] photoBytes) throws Exception { 7938 AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() { 7939 @Override 7940 protected Object doInBackground(Object... params) { 7941 OutputStream os; 7942 try { 7943 os = mResolver.openOutputStream(uri, "rw"); 7944 os.write(photoBytes); 7945 os.close(); 7946 return null; 7947 } catch (IOException ioe) { 7948 throw new RuntimeException(ioe); 7949 } 7950 } 7951 }; 7952 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null).get(); 7953 } 7954 7955 @Test testPhotoDimensionLimits()7956 public void testPhotoDimensionLimits() { 7957 ContentValues values = new ContentValues(); 7958 values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256); 7959 values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96); 7960 assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values); 7961 } 7962 7963 @Test testPhotoStoreCleanup()7964 public void testPhotoStoreCleanup() throws IOException { 7965 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 7966 PhotoStore photoStore = provider.getPhotoStore(); 7967 7968 // Trigger an initial cleanup so another one won't happen while we're running this test. 7969 provider.cleanupPhotoStore(); 7970 7971 // Insert a couple of contacts with photos. 7972 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver); 7973 long contactId1 = queryContactId(rawContactId1); 7974 long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal)); 7975 long photoFileId1 = 7976 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1), 7977 Photo.PHOTO_FILE_ID); 7978 7979 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver); 7980 long contactId2 = queryContactId(rawContactId2); 7981 long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal)); 7982 long photoFileId2 = 7983 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2), 7984 Photo.PHOTO_FILE_ID); 7985 7986 // Update the second raw contact with a different photo. 7987 ContentValues values = new ContentValues(); 7988 values.put(Data.RAW_CONTACT_ID, rawContactId2); 7989 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 7990 values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL)); 7991 assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?", 7992 new String[]{String.valueOf(dataId2)})); 7993 long replacementPhotoFileId = 7994 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2), 7995 Photo.PHOTO_FILE_ID); 7996 7997 // Insert a third raw contact that has a bogus photo file ID. 7998 long bogusFileId = 1234567; 7999 long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver); 8000 long contactId3 = queryContactId(rawContactId3); 8001 values.clear(); 8002 values.put(Data.RAW_CONTACT_ID, rawContactId3); 8003 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 8004 values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal, 8005 PhotoSize.THUMBNAIL)); 8006 values.put(Photo.PHOTO_FILE_ID, bogusFileId); 8007 values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true); 8008 mResolver.insert(Data.CONTENT_URI, values); 8009 8010 // Insert a fourth raw contact with a stream item that has a photo, then remove that photo 8011 // from the photo store. 8012 Account socialAccount = new Account("social", "social"); 8013 long rawContactId4 = RawContactUtil.createRawContactWithName(mResolver, socialAccount); 8014 Uri streamItemUri = 8015 insertStreamItem(rawContactId4, buildGenericStreamItemValues(), socialAccount); 8016 long streamItemId = ContentUris.parseId(streamItemUri); 8017 Uri streamItemPhotoUri = insertStreamItemPhoto( 8018 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount); 8019 long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri, 8020 StreamItemPhotos.PHOTO_FILE_ID); 8021 photoStore.remove(streamItemPhotoFileId); 8022 8023 // Also insert a bogus photo that nobody is using. 8024 long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource( 8025 R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96)); 8026 8027 // Manually trigger another cleanup in the provider. 8028 provider.cleanupPhotoStore(); 8029 8030 // The following things should have happened. 8031 8032 // 1. Raw contact 1 and its photo remain unaffected. 8033 assertEquals(photoFileId1, (long) getStoredLongValue( 8034 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1), 8035 Contacts.PHOTO_FILE_ID)); 8036 8037 // 2. Raw contact 2 retains its new photo. The old one is deleted from the photo store. 8038 assertEquals(replacementPhotoFileId, (long) getStoredLongValue( 8039 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2), 8040 Contacts.PHOTO_FILE_ID)); 8041 assertNull(photoStore.get(photoFileId2)); 8042 8043 // 3. Raw contact 3 should have its photo file reference cleared. 8044 assertNull(getStoredValue( 8045 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3), 8046 Contacts.PHOTO_FILE_ID)); 8047 8048 // 4. The bogus photo that nobody was using should be cleared from the photo store. 8049 assertNull(photoStore.get(bogusPhotoId)); 8050 8051 // 5. The bogus stream item photo should be cleared from the stream item. 8052 assertStoredValues(Uri.withAppendedPath( 8053 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 8054 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 8055 new ContentValues[0]); 8056 } 8057 8058 @Test testPhotoStoreCleanupForProfile()8059 public void testPhotoStoreCleanupForProfile() { 8060 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 8061 PhotoStore profilePhotoStore = provider.getProfilePhotoStore(); 8062 8063 // Trigger an initial cleanup so another one won't happen while we're running this test. 8064 provider.switchToProfileModeForTest(); 8065 provider.cleanupPhotoStore(); 8066 8067 // Create the profile contact and add a photo. 8068 Account socialAccount = new Account("social", "social"); 8069 ContentValues values = new ContentValues(); 8070 values.put(RawContacts.ACCOUNT_NAME, socialAccount.name); 8071 values.put(RawContacts.ACCOUNT_TYPE, socialAccount.type); 8072 long profileRawContactId = createBasicProfileContact(values); 8073 long profileContactId = queryContactId(profileRawContactId); 8074 long dataId = ContentUris.parseId( 8075 insertPhoto(profileRawContactId, R.drawable.earth_normal)); 8076 long profilePhotoFileId = 8077 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), 8078 Photo.PHOTO_FILE_ID); 8079 8080 // Also add a stream item with a photo. 8081 Uri streamItemUri = 8082 insertStreamItem(profileRawContactId, buildGenericStreamItemValues(), 8083 socialAccount); 8084 long streamItemId = ContentUris.parseId(streamItemUri); 8085 Uri streamItemPhotoUri = insertStreamItemPhoto( 8086 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount); 8087 long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri, 8088 StreamItemPhotos.PHOTO_FILE_ID); 8089 8090 // Remove the stream item photo and the profile photo. 8091 profilePhotoStore.remove(profilePhotoFileId); 8092 profilePhotoStore.remove(streamItemPhotoFileId); 8093 8094 // Manually trigger another cleanup in the provider. 8095 provider.switchToProfileModeForTest(); 8096 provider.cleanupPhotoStore(); 8097 8098 // The following things should have happened. 8099 8100 // The stream item photo should have been removed. 8101 assertStoredValues(Uri.withAppendedPath( 8102 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 8103 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 8104 new ContentValues[0]); 8105 8106 // The profile photo should have been cleared. 8107 assertNull(getStoredValue( 8108 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId), 8109 Contacts.PHOTO_FILE_ID)); 8110 8111 } 8112 8113 @Test testCleanupDanglingContacts_noDanglingContacts()8114 public void testCleanupDanglingContacts_noDanglingContacts() throws Exception { 8115 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 8116 RawContactUtil.createRawContactWithName(mResolver, "A", "B"); 8117 RawContactUtil.createRawContactWithName(mResolver, "C", "D"); 8118 8119 provider.cleanupDanglingContacts(); 8120 8121 Cursor contactCursor = mResolver.query(Contacts.CONTENT_URI, null, null, null, null); 8122 Cursor rawContactCursor = mResolver.query(RawContacts.CONTENT_URI, null, null, null, null); 8123 8124 // No contacts should be deleted 8125 assertEquals(2, contactCursor.getCount()); 8126 assertEquals(2, rawContactCursor.getCount()); 8127 } 8128 8129 @Test testCleanupDanglingContacts_singleDanglingContacts()8130 public void testCleanupDanglingContacts_singleDanglingContacts() throws Exception { 8131 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 8132 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "A", "B"); 8133 8134 // Change the contact_id to create dangling contact. 8135 SQLiteDatabase db = provider.getDatabaseHelper().getWritableDatabase(); 8136 db.execSQL("UPDATE raw_contacts SET contact_id = 99999 WHERE _id = " + rawContactId + ";"); 8137 8138 provider.cleanupDanglingContacts(); 8139 8140 // Dangling contact should be deleted from contacts table. 8141 assertEquals(0, mResolver.query(Contacts.CONTENT_URI, null, null, null, null).getCount()); 8142 } 8143 8144 @Test testCleanupDanglingContacts_multipleDanglingContacts()8145 public void testCleanupDanglingContacts_multipleDanglingContacts() throws Exception { 8146 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 8147 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "A", "B"); 8148 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "C", "D"); 8149 RawContactUtil.createRawContactWithName(mResolver, "E", "F"); 8150 8151 final ContactsDatabaseHelper helper = provider.getDatabaseHelper(); 8152 SQLiteDatabase db = helper.getWritableDatabase(); 8153 8154 // Change contact_id of RawContact1 and RawContact2 to create dangling contacts. 8155 db.execSQL("UPDATE raw_contacts SET contact_id = 99998 WHERE _id = " + rawContactId1 + ";"); 8156 db.execSQL("UPDATE raw_contacts SET contact_id = 99999 WHERE _id = " + rawContactId2 + ";"); 8157 8158 provider.cleanupDanglingContacts(); 8159 8160 // Should only be one contact left in the contacts table. 8161 // RawContact1 and RawContact2 should be deleted from the contacts table. 8162 assertEquals(1, mResolver.query(Contacts.CONTENT_URI, null, null, null, null).getCount()); 8163 } 8164 8165 @Test testOverwritePhotoWithThumbnail()8166 public void testOverwritePhotoWithThumbnail() throws IOException { 8167 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 8168 long contactId = queryContactId(rawContactId); 8169 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 8170 8171 // Write a regular-size photo. 8172 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 8173 Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID); 8174 assertTrue(photoFileId != null && photoFileId > 0); 8175 8176 // Now overwrite the photo with a thumbnail-sized photo. 8177 ContentValues update = new ContentValues(); 8178 update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL)); 8179 mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null); 8180 8181 // Photo file ID should have been nulled out, and the photo URI should be the same as the 8182 // thumbnail URI. 8183 assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID)); 8184 String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI); 8185 String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI); 8186 assertEquals(photoUri, thumbnailUri); 8187 8188 // Retrieving the photo URI should get the thumbnail content. 8189 EvenMoreAsserts.assertImageRawData(getContext(), 8190 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL), 8191 mResolver.openInputStream(Uri.parse(photoUri))); 8192 } 8193 8194 @Test testUpdateRawContactSetStarred()8195 public void testUpdateRawContactSetStarred() { 8196 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver); 8197 Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 8198 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver); 8199 Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); 8200 setAggregationException( 8201 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 8202 8203 long contactId = queryContactId(rawContactId1); 8204 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 8205 assertStoredValue(contactUri, Contacts.STARRED, "0"); 8206 8207 assertDirty(rawContactUri1, true); 8208 assertDirty(rawContactUri2, true); 8209 clearDirty(rawContactUri1); 8210 clearDirty(rawContactUri2); 8211 8212 ContentValues values = new ContentValues(); 8213 values.put(RawContacts.STARRED, "1"); 8214 8215 mResolver.update(rawContactUri1, values, null, null); 8216 8217 assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); 8218 assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); 8219 assertStoredValue(contactUri, Contacts.STARRED, "1"); 8220 assertDirty(rawContactUri1, true); 8221 assertNetworkNotified(true); 8222 8223 clearDirty(rawContactUri1); 8224 values.put(RawContacts.STARRED, "0"); 8225 mResolver.update(rawContactUri1, values, null, null); 8226 8227 assertStoredValue(rawContactUri1, RawContacts.STARRED, "0"); 8228 assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); 8229 assertStoredValue(contactUri, Contacts.STARRED, "0"); 8230 assertDirty(rawContactUri1, true); 8231 assertNetworkNotified(true); 8232 8233 clearDirty(rawContactUri1); 8234 values.put(Contacts.STARRED, "1"); 8235 mResolver.update(contactUri, values, null, null); 8236 8237 assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); 8238 assertStoredValue(rawContactUri2, RawContacts.STARRED, "1"); 8239 assertStoredValue(contactUri, Contacts.STARRED, "1"); 8240 assertDirty(rawContactUri1, true); 8241 assertNetworkNotified(true); 8242 } 8243 8244 @Test testUpdateContactOptionsSetStarred()8245 public void testUpdateContactOptionsSetStarred() { 8246 long rawContactId = RawContactUtil.createRawContact(mResolver); 8247 long contactId = queryContactId(rawContactId); 8248 String lookupKey = queryLookupKey(contactId); 8249 ContentValues values =new ContentValues(); 8250 values.put(Contacts.STARRED, 1); 8251 8252 Uri contactLookupUri = ContentUris.withAppendedId( 8253 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId); 8254 mResolver.update(contactLookupUri, values, null, null); 8255 assertNetworkNotified(true); 8256 } 8257 8258 @Test testSetAndClearSuperPrimaryEmail()8259 public void testSetAndClearSuperPrimaryEmail() { 8260 long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 8261 Uri mailUri11 = insertEmail(rawContactId1, "[email protected]"); 8262 Uri mailUri12 = insertEmail(rawContactId1, "[email protected]"); 8263 8264 long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b")); 8265 Uri mailUri21 = insertEmail(rawContactId2, "[email protected]"); 8266 Uri mailUri22 = insertEmail(rawContactId2, "[email protected]"); 8267 8268 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 8269 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 8270 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 8271 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 8272 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8273 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8274 assertStoredValue(mailUri22, Data.IS_PRIMARY, 0); 8275 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0); 8276 8277 // Set super primary on the first pair, primary on the second 8278 { 8279 ContentValues values = new ContentValues(); 8280 values.put(Data.IS_SUPER_PRIMARY, 1); 8281 mResolver.update(mailUri11, values, null, null); 8282 } 8283 { 8284 ContentValues values = new ContentValues(); 8285 values.put(Data.IS_SUPER_PRIMARY, 1); 8286 mResolver.update(mailUri22, values, null, null); 8287 } 8288 8289 assertStoredValue(mailUri11, Data.IS_PRIMARY, 1); 8290 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1); 8291 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 8292 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 8293 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8294 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8295 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 8296 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 8297 8298 // Clear primary on the first pair, make sure second is not affected and super_primary is 8299 // also cleared 8300 { 8301 ContentValues values = new ContentValues(); 8302 values.put(Data.IS_PRIMARY, 0); 8303 mResolver.update(mailUri11, values, null, null); 8304 } 8305 8306 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 8307 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 8308 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 8309 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 8310 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8311 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8312 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 8313 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 8314 8315 // Ensure that we can only clear super_primary, if we specify the correct data row 8316 { 8317 ContentValues values = new ContentValues(); 8318 values.put(Data.IS_SUPER_PRIMARY, 0); 8319 mResolver.update(mailUri21, values, null, null); 8320 } 8321 8322 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8323 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8324 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 8325 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 8326 8327 // Ensure that we can only clear primary, if we specify the correct data row 8328 { 8329 ContentValues values = new ContentValues(); 8330 values.put(Data.IS_PRIMARY, 0); 8331 mResolver.update(mailUri21, values, null, null); 8332 } 8333 8334 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8335 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8336 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 8337 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 8338 8339 // Now clear super-primary for real 8340 { 8341 ContentValues values = new ContentValues(); 8342 values.put(Data.IS_SUPER_PRIMARY, 0); 8343 mResolver.update(mailUri22, values, null, null); 8344 } 8345 8346 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 8347 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 8348 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 8349 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 8350 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 8351 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 8352 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 8353 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0); 8354 } 8355 8356 /** 8357 * Common function for the testNewPrimaryIn* functions. Its four configurations 8358 * are each called from its own test 8359 */ testChangingPrimary(boolean inUpdate, boolean withSuperPrimary)8360 public void testChangingPrimary(boolean inUpdate, boolean withSuperPrimary) { 8361 long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a")); 8362 Uri mailUri1 = insertEmail(rawContactId, "[email protected]", true); 8363 8364 if (withSuperPrimary) { 8365 final ContentValues values = new ContentValues(); 8366 values.put(Data.IS_SUPER_PRIMARY, 1); 8367 mResolver.update(mailUri1, values, null, null); 8368 } 8369 8370 assertStoredValue(mailUri1, Data.IS_PRIMARY, 1); 8371 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 8372 8373 // Insert another item 8374 final Uri mailUri2; 8375 if (inUpdate) { 8376 mailUri2 = insertEmail(rawContactId, "[email protected]"); 8377 8378 assertStoredValue(mailUri1, Data.IS_PRIMARY, 1); 8379 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 8380 assertStoredValue(mailUri2, Data.IS_PRIMARY, 0); 8381 assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, 0); 8382 8383 final ContentValues values = new ContentValues(); 8384 values.put(Data.IS_PRIMARY, 1); 8385 mResolver.update(mailUri2, values, null, null); 8386 } else { 8387 // directly add as default 8388 mailUri2 = insertEmail(rawContactId, "[email protected]", true); 8389 } 8390 8391 // Ensure that primary has been unset on the first 8392 // If withSuperPrimary is set, also ensure that is has been moved to the new item 8393 assertStoredValue(mailUri1, Data.IS_PRIMARY, 0); 8394 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0); 8395 assertStoredValue(mailUri2, Data.IS_PRIMARY, 1); 8396 assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 8397 } 8398 8399 @Test testNewPrimaryInInsert()8400 public void testNewPrimaryInInsert() { 8401 testChangingPrimary(false, false); 8402 } 8403 8404 @Test testNewPrimaryInInsertWithSuperPrimary()8405 public void testNewPrimaryInInsertWithSuperPrimary() { 8406 testChangingPrimary(false, true); 8407 } 8408 8409 @Test testNewPrimaryInUpdate()8410 public void testNewPrimaryInUpdate() { 8411 testChangingPrimary(true, false); 8412 } 8413 8414 @Test testNewPrimaryInUpdateWithSuperPrimary()8415 public void testNewPrimaryInUpdateWithSuperPrimary() { 8416 testChangingPrimary(true, true); 8417 } 8418 8419 @Test testContactSortOrder()8420 public void testContactSortOrder() { 8421 assertEquals(ContactsColumns.PHONEBOOK_BUCKET_PRIMARY + ", " 8422 + Contacts.SORT_KEY_PRIMARY, 8423 ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_PRIMARY)); 8424 assertEquals(ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + ", " 8425 + Contacts.SORT_KEY_ALTERNATIVE, 8426 ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_ALTERNATIVE)); 8427 assertEquals(ContactsColumns.PHONEBOOK_BUCKET_PRIMARY + " DESC, " 8428 + Contacts.SORT_KEY_PRIMARY + " DESC", 8429 ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_PRIMARY + " DESC")); 8430 String suffix = " COLLATE LOCALIZED DESC"; 8431 assertEquals(ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + suffix 8432 + ", " + Contacts.SORT_KEY_ALTERNATIVE + suffix, 8433 ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_ALTERNATIVE 8434 + suffix)); 8435 } 8436 8437 @Test testContactCounts()8438 public void testContactCounts() { 8439 Uri uri = Contacts.CONTENT_URI.buildUpon() 8440 .appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true").build(); 8441 8442 RawContactUtil.createRawContact(mResolver); 8443 RawContactUtil.createRawContactWithName(mResolver, "James", "Sullivan"); 8444 RawContactUtil.createRawContactWithName(mResolver, "The Abominable", "Snowman"); 8445 RawContactUtil.createRawContactWithName(mResolver, "Mike", "Wazowski"); 8446 RawContactUtil.createRawContactWithName(mResolver, "randall", "boggs"); 8447 RawContactUtil.createRawContactWithName(mResolver, "Boo", null); 8448 RawContactUtil.createRawContactWithName(mResolver, "Mary", null); 8449 RawContactUtil.createRawContactWithName(mResolver, "Roz", null); 8450 // Contacts with null display names get sorted to the end (using the number bucket) 8451 RawContactUtil.createRawContactWithName(mResolver, null, null); 8452 8453 Cursor cursor = mResolver.query(uri, 8454 new String[]{Contacts.DISPLAY_NAME}, 8455 null, null, Contacts.SORT_KEY_PRIMARY); 8456 8457 assertFirstLetterValues(cursor, "B", "J", "M", "R", "T", "#"); 8458 assertFirstLetterCounts(cursor, 1, 1, 2, 2, 1, 2); 8459 cursor.close(); 8460 8461 cursor = mResolver.query(uri, 8462 new String[]{Contacts.DISPLAY_NAME}, 8463 null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC"); 8464 8465 assertFirstLetterValues(cursor, "#", "W", "S", "R", "M", "B"); 8466 assertFirstLetterCounts(cursor, 2, 1, 2, 1, 1, 2); 8467 cursor.close(); 8468 } 8469 assertFirstLetterValues(Cursor cursor, String... expected)8470 private void assertFirstLetterValues(Cursor cursor, String... expected) { 8471 String[] actual = cursor.getExtras() 8472 .getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); 8473 MoreAsserts.assertEquals(expected, actual); 8474 } 8475 assertFirstLetterCounts(Cursor cursor, int... expected)8476 private void assertFirstLetterCounts(Cursor cursor, int... expected) { 8477 int[] actual = cursor.getExtras() 8478 .getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); 8479 MoreAsserts.assertEquals(expected, actual); 8480 } 8481 8482 @Test testReadBooleanQueryParameter()8483 public void testReadBooleanQueryParameter() { 8484 assertBooleanUriParameter("foo:bar", "bool", true, true); 8485 assertBooleanUriParameter("foo:bar", "bool", false, false); 8486 assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false); 8487 assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true); 8488 assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false); 8489 assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true); 8490 assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false); 8491 assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false); 8492 assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true); 8493 assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true); 8494 assertBooleanUriParameter("foo:bar?bool", "bool", true, true); 8495 } 8496 assertBooleanUriParameter(String uriString, String parameter, boolean defaultValue, boolean expectedValue)8497 private void assertBooleanUriParameter(String uriString, String parameter, 8498 boolean defaultValue, boolean expectedValue) { 8499 assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter( 8500 Uri.parse(uriString), parameter, defaultValue)); 8501 } 8502 8503 @Test testGetQueryParameter()8504 public void testGetQueryParameter() { 8505 assertQueryParameter("foo:bar", "param", null); 8506 assertQueryParameter("foo:bar?param", "param", null); 8507 assertQueryParameter("foo:bar?param=", "param", ""); 8508 assertQueryParameter("foo:bar?param=val", "param", "val"); 8509 assertQueryParameter("foo:bar?param=val&some=some", "param", "val"); 8510 assertQueryParameter("foo:bar?some=some¶m=val", "param", "val"); 8511 assertQueryParameter("foo:bar?some=some¶m=val&else=else", "param", "val"); 8512 assertQueryParameter("foo:bar?param=john%40doe.com", "param", "[email protected]"); 8513 assertQueryParameter("foo:bar?some_param=val", "param", null); 8514 assertQueryParameter("foo:bar?some_param=val1¶m=val2", "param", "val2"); 8515 assertQueryParameter("foo:bar?some_param=val1¶m=", "param", ""); 8516 assertQueryParameter("foo:bar?some_param=val1¶m", "param", null); 8517 assertQueryParameter("foo:bar?some_param=val1&another_param=val2¶m=val3", 8518 "param", "val3"); 8519 assertQueryParameter("foo:bar?some_param=val1¶m=val2&some_param=val3", 8520 "param", "val2"); 8521 assertQueryParameter("foo:bar?param=val1&some_param=val2", "param", "val1"); 8522 assertQueryParameter("foo:bar?p=val1&pp=val2", "p", "val1"); 8523 assertQueryParameter("foo:bar?pp=val1&p=val2", "p", "val2"); 8524 assertQueryParameter("foo:bar?ppp=val1&pp=val2&p=val3", "p", "val3"); 8525 assertQueryParameter("foo:bar?ppp=val&", "p", null); 8526 } 8527 8528 @Test testMissingAccountTypeParameter()8529 public void testMissingAccountTypeParameter() { 8530 // Try querying for RawContacts only using ACCOUNT_NAME 8531 final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter( 8532 RawContacts.ACCOUNT_NAME, "lolwut").build(); 8533 try { 8534 final Cursor cursor = mResolver.query(queryUri, null, null, null, null); 8535 fail("Able to query with incomplete account query parameters"); 8536 } catch (IllegalArgumentException e) { 8537 // Expected behavior. 8538 } 8539 } 8540 8541 @Test testInsertInconsistentAccountType()8542 public void testInsertInconsistentAccountType() { 8543 // Try inserting RawContact with inconsistent Accounts 8544 final Account red = new Account("red", "red"); 8545 final Account blue = new Account("blue", "blue"); 8546 8547 final ContentValues values = new ContentValues(); 8548 values.put(RawContacts.ACCOUNT_NAME, red.name); 8549 values.put(RawContacts.ACCOUNT_TYPE, red.type); 8550 8551 final Uri insertUri = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, 8552 blue); 8553 try { 8554 mResolver.insert(insertUri, values); 8555 fail("Able to insert RawContact with inconsistent account details"); 8556 } catch (IllegalArgumentException e) { 8557 // Expected behavior. 8558 } 8559 } 8560 8561 @Test testProviderStatusNoContactsNoAccounts()8562 public void testProviderStatusNoContactsNoAccounts() throws Exception { 8563 assertProviderStatus(ProviderStatus.STATUS_EMPTY); 8564 } 8565 8566 @Test testProviderStatusOnlyLocalContacts()8567 public void testProviderStatusOnlyLocalContacts() throws Exception { 8568 long rawContactId = RawContactUtil.createRawContact(mResolver); 8569 assertProviderStatus(ProviderStatus.STATUS_NORMAL); 8570 mResolver.delete( 8571 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), null, null); 8572 assertProviderStatus(ProviderStatus.STATUS_EMPTY); 8573 } 8574 8575 @Test testProviderStatusWithAccounts()8576 public void testProviderStatusWithAccounts() throws Exception { 8577 assertProviderStatus(ProviderStatus.STATUS_EMPTY); 8578 mActor.setAccounts(new Account[]{TestUtil.ACCOUNT_1}); 8579 ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[]{TestUtil.ACCOUNT_1}); 8580 assertProviderStatus(ProviderStatus.STATUS_NORMAL); 8581 mActor.setAccounts(new Account[0]); 8582 ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[0]); 8583 assertProviderStatus(ProviderStatus.STATUS_EMPTY); 8584 } 8585 assertProviderStatus(int expectedProviderStatus)8586 private void assertProviderStatus(int expectedProviderStatus) { 8587 Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI, 8588 new String[]{ProviderStatus.STATUS}, null, null, 8589 null); 8590 assertTrue(cursor.moveToFirst()); 8591 assertEquals(expectedProviderStatus, cursor.getInt(0)); 8592 cursor.close(); 8593 } 8594 8595 @Test testProperties()8596 public void testProperties() throws Exception { 8597 ContactsProvider2 provider = (ContactsProvider2)getProvider(); 8598 ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper(); 8599 assertNull(helper.getProperty("non-existent", null)); 8600 assertEquals("default", helper.getProperty("non-existent", "default")); 8601 8602 helper.setProperty("existent1", "string1"); 8603 helper.setProperty("existent2", "string2"); 8604 assertEquals("string1", helper.getProperty("existent1", "default")); 8605 assertEquals("string2", helper.getProperty("existent2", "default")); 8606 helper.setProperty("existent1", null); 8607 assertEquals("default", helper.getProperty("existent1", "default")); 8608 } 8609 8610 @Test testQueryMultiVCard()8611 public void testQueryMultiVCard() { 8612 // No need to create any contacts here, because the query for multiple vcards 8613 // does not go into the database at all 8614 Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456")); 8615 Cursor cursor = mResolver.query(uri, null, null, null, null); 8616 assertEquals(1, cursor.getCount()); 8617 assertTrue(cursor.moveToFirst()); 8618 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 8619 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 8620 8621 // The resulting name contains date and time. Ensure that before and after are correct 8622 assertTrue(filename.startsWith("vcards_")); 8623 assertTrue(filename.endsWith(".vcf")); 8624 cursor.close(); 8625 } 8626 8627 @Test testQueryFileSingleVCard()8628 public void testQueryFileSingleVCard() { 8629 final VCardTestUriCreator contacts = createVCardTestContacts(); 8630 8631 { 8632 Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null); 8633 assertEquals(1, cursor.getCount()); 8634 assertTrue(cursor.moveToFirst()); 8635 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 8636 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 8637 assertEquals("John Doe.vcf", filename); 8638 cursor.close(); 8639 } 8640 8641 { 8642 Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null); 8643 assertEquals(1, cursor.getCount()); 8644 assertTrue(cursor.moveToFirst()); 8645 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 8646 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 8647 assertEquals("Jane Doh.vcf", filename); 8648 cursor.close(); 8649 } 8650 } 8651 8652 @Test testQueryFileProfileVCard()8653 public void testQueryFileProfileVCard() { 8654 createBasicProfileContact(new ContentValues()); 8655 Cursor cursor = mResolver.query(Profile.CONTENT_VCARD_URI, null, null, null, null); 8656 assertEquals(1, cursor.getCount()); 8657 assertTrue(cursor.moveToFirst()); 8658 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 8659 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 8660 assertEquals("Mia Prophyl.vcf", filename); 8661 cursor.close(); 8662 } 8663 8664 @Test testOpenAssetFileMultiVCard()8665 public void testOpenAssetFileMultiVCard() throws IOException { 8666 final VCardTestUriCreator contacts = createVCardTestContacts(); 8667 8668 final AssetFileDescriptor descriptor = 8669 mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r"); 8670 final FileInputStream inputStream = descriptor.createInputStream(); 8671 String data = readToEnd(inputStream); 8672 inputStream.close(); 8673 descriptor.close(); 8674 8675 // Ensure that the resulting VCard has both contacts 8676 assertTrue(data.contains("N:Doe;John;;;")); 8677 assertTrue(data.contains("N:Doh;Jane;;;")); 8678 } 8679 8680 @Test testOpenAssetFileSingleVCard()8681 public void testOpenAssetFileSingleVCard() throws IOException { 8682 final VCardTestUriCreator contacts = createVCardTestContacts(); 8683 8684 // Ensure that the right VCard is being created in each case 8685 { 8686 final AssetFileDescriptor descriptor = 8687 mResolver.openAssetFileDescriptor(contacts.getUri1(), "r"); 8688 final FileInputStream inputStream = descriptor.createInputStream(); 8689 final String data = readToEnd(inputStream); 8690 inputStream.close(); 8691 descriptor.close(); 8692 8693 assertTrue(data.contains("N:Doe;John;;;")); 8694 assertFalse(data.contains("N:Doh;Jane;;;")); 8695 } 8696 8697 { 8698 final AssetFileDescriptor descriptor = 8699 mResolver.openAssetFileDescriptor(contacts.getUri2(), "r"); 8700 final FileInputStream inputStream = descriptor.createInputStream(); 8701 final String data = readToEnd(inputStream); 8702 inputStream.close(); 8703 descriptor.close(); 8704 8705 assertFalse(data.contains("N:Doe;John;;;")); 8706 assertTrue(data.contains("N:Doh;Jane;;;")); 8707 } 8708 } 8709 8710 @Test testAutoGroupMembership()8711 public void testAutoGroupMembership() { 8712 long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */); 8713 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 8714 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */); 8715 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */); 8716 long r1 = RawContactUtil.createRawContact(mResolver, mAccount); 8717 long r2 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8718 long r3 = RawContactUtil.createRawContact(mResolver, null); 8719 8720 Cursor c = queryGroupMemberships(mAccount); 8721 try { 8722 assertTrue(c.moveToNext()); 8723 assertEquals(g1, c.getLong(0)); 8724 assertEquals(r1, c.getLong(1)); 8725 assertFalse(c.moveToNext()); 8726 } finally { 8727 c.close(); 8728 } 8729 8730 c = queryGroupMemberships(mAccountTwo); 8731 try { 8732 assertTrue(c.moveToNext()); 8733 assertEquals(g3, c.getLong(0)); 8734 assertEquals(r2, c.getLong(1)); 8735 assertFalse(c.moveToNext()); 8736 } finally { 8737 c.close(); 8738 } 8739 } 8740 8741 @Test testNoAutoAddMembershipAfterGroupCreation()8742 public void testNoAutoAddMembershipAfterGroupCreation() { 8743 long r1 = RawContactUtil.createRawContact(mResolver, mAccount); 8744 long r2 = RawContactUtil.createRawContact(mResolver, mAccount); 8745 long r3 = RawContactUtil.createRawContact(mResolver, mAccount); 8746 long r4 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8747 long r5 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8748 long r6 = RawContactUtil.createRawContact(mResolver, null); 8749 8750 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8751 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8752 8753 long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */); 8754 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 8755 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */); 8756 8757 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8758 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8759 } 8760 8761 // create some starred and non-starred contacts, some associated with account, some not 8762 // favorites group created 8763 // the starred contacts should be added to group 8764 // favorites group removed 8765 // no change to starred status 8766 @Test testFavoritesMembershipAfterGroupCreation()8767 public void testFavoritesMembershipAfterGroupCreation() { 8768 long r1 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1"); 8769 long r2 = RawContactUtil.createRawContact(mResolver, mAccount); 8770 long r3 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1"); 8771 long r4 = RawContactUtil.createRawContact(mResolver, mAccountTwo, RawContacts.STARRED, "1"); 8772 long r5 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8773 long r6 = RawContactUtil.createRawContact(mResolver, null, RawContacts.STARRED, "1"); 8774 long r7 = RawContactUtil.createRawContact(mResolver, null); 8775 8776 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8777 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8778 8779 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 8780 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 8781 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */); 8782 8783 assertTrue(queryRawContactIsStarred(r1)); 8784 assertFalse(queryRawContactIsStarred(r2)); 8785 assertTrue(queryRawContactIsStarred(r3)); 8786 assertTrue(queryRawContactIsStarred(r4)); 8787 assertFalse(queryRawContactIsStarred(r5)); 8788 assertTrue(queryRawContactIsStarred(r6)); 8789 assertFalse(queryRawContactIsStarred(r7)); 8790 8791 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8792 Cursor c = queryGroupMemberships(mAccount); 8793 try { 8794 assertTrue(c.moveToNext()); 8795 assertEquals(g1, c.getLong(0)); 8796 assertEquals(r1, c.getLong(1)); 8797 assertTrue(c.moveToNext()); 8798 assertEquals(g1, c.getLong(0)); 8799 assertEquals(r3, c.getLong(1)); 8800 assertFalse(c.moveToNext()); 8801 } finally { 8802 c.close(); 8803 } 8804 8805 updateItem(RawContacts.CONTENT_URI, r6, 8806 RawContacts.ACCOUNT_NAME, mAccount.name, 8807 RawContacts.ACCOUNT_TYPE, mAccount.type); 8808 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8809 c = queryGroupMemberships(mAccount); 8810 try { 8811 assertTrue(c.moveToNext()); 8812 assertEquals(g1, c.getLong(0)); 8813 assertEquals(r1, c.getLong(1)); 8814 assertTrue(c.moveToNext()); 8815 assertEquals(g1, c.getLong(0)); 8816 assertEquals(r3, c.getLong(1)); 8817 assertTrue(c.moveToNext()); 8818 assertEquals(g1, c.getLong(0)); 8819 assertEquals(r6, c.getLong(1)); 8820 assertFalse(c.moveToNext()); 8821 } finally { 8822 c.close(); 8823 } 8824 8825 mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null); 8826 8827 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8828 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8829 8830 assertTrue(queryRawContactIsStarred(r1)); 8831 assertFalse(queryRawContactIsStarred(r2)); 8832 assertTrue(queryRawContactIsStarred(r3)); 8833 assertTrue(queryRawContactIsStarred(r4)); 8834 assertFalse(queryRawContactIsStarred(r5)); 8835 assertTrue(queryRawContactIsStarred(r6)); 8836 assertFalse(queryRawContactIsStarred(r7)); 8837 } 8838 8839 @Test testFavoritesGroupMembershipChangeAfterStarChange()8840 public void testFavoritesGroupMembershipChangeAfterStarChange() { 8841 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 8842 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */); 8843 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */); 8844 long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */); 8845 long r1 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1"); 8846 long r2 = RawContactUtil.createRawContact(mResolver, mAccount); 8847 long r3 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8848 8849 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8850 Cursor c = queryGroupMemberships(mAccount); 8851 try { 8852 assertTrue(c.moveToNext()); 8853 assertEquals(g1, c.getLong(0)); 8854 assertEquals(r1, c.getLong(1)); 8855 assertFalse(c.moveToNext()); 8856 } finally { 8857 c.close(); 8858 } 8859 8860 // remove the star from r1 8861 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0")); 8862 8863 // Since no raw contacts are starred, there should be no group memberships. 8864 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8865 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8866 8867 // mark r1 as starred 8868 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1")); 8869 // Now that r1 is starred it should have a membership in the one groups from mAccount 8870 // that is marked as a favorite. 8871 // There should be no memberships in mAccountTwo since it has no starred raw contacts. 8872 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8873 c = queryGroupMemberships(mAccount); 8874 try { 8875 assertTrue(c.moveToNext()); 8876 assertEquals(g1, c.getLong(0)); 8877 assertEquals(r1, c.getLong(1)); 8878 assertFalse(c.moveToNext()); 8879 } finally { 8880 c.close(); 8881 } 8882 8883 // remove the star from r1 8884 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0")); 8885 // Since no raw contacts are starred, there should be no group memberships. 8886 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8887 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8888 8889 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1)); 8890 assertNotNull(contactUri); 8891 8892 // mark r1 as starred via its contact lookup uri 8893 assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1")); 8894 // Now that r1 is starred it should have a membership in the one groups from mAccount 8895 // that is marked as a favorite. 8896 // There should be no memberships in mAccountTwo since it has no starred raw contacts. 8897 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8898 c = queryGroupMemberships(mAccount); 8899 try { 8900 assertTrue(c.moveToNext()); 8901 assertEquals(g1, c.getLong(0)); 8902 assertEquals(r1, c.getLong(1)); 8903 assertFalse(c.moveToNext()); 8904 } finally { 8905 c.close(); 8906 } 8907 8908 // remove the star from r1 8909 updateItem(contactUri, Contacts.STARRED, "0"); 8910 // Since no raw contacts are starred, there should be no group memberships. 8911 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8912 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8913 } 8914 8915 @Test testStarChangedAfterGroupMembershipChange()8916 public void testStarChangedAfterGroupMembershipChange() { 8917 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 8918 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */); 8919 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */); 8920 long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */); 8921 long r1 = RawContactUtil.createRawContact(mResolver, mAccount); 8922 long r2 = RawContactUtil.createRawContact(mResolver, mAccount); 8923 long r3 = RawContactUtil.createRawContact(mResolver, mAccountTwo); 8924 8925 assertFalse(queryRawContactIsStarred(r1)); 8926 assertFalse(queryRawContactIsStarred(r2)); 8927 assertFalse(queryRawContactIsStarred(r3)); 8928 8929 Cursor c; 8930 8931 // add r1 to one favorites group 8932 // r1's star should automatically be set 8933 // r1 should automatically be added to the other favorites group 8934 Uri urir1g1 = insertGroupMembership(r1, g1); 8935 assertTrue(queryRawContactIsStarred(r1)); 8936 assertFalse(queryRawContactIsStarred(r2)); 8937 assertFalse(queryRawContactIsStarred(r3)); 8938 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8939 c = queryGroupMemberships(mAccount); 8940 try { 8941 assertTrue(c.moveToNext()); 8942 assertEquals(g1, c.getLong(0)); 8943 assertEquals(r1, c.getLong(1)); 8944 assertFalse(c.moveToNext()); 8945 } finally { 8946 c.close(); 8947 } 8948 8949 // remove r1 from one favorites group 8950 mResolver.delete(urir1g1, null, null); 8951 // r1's star should no longer be set 8952 assertFalse(queryRawContactIsStarred(r1)); 8953 assertFalse(queryRawContactIsStarred(r2)); 8954 assertFalse(queryRawContactIsStarred(r3)); 8955 // there should be no membership rows 8956 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8957 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8958 8959 // add r3 to the one favorites group for that account 8960 // r3's star should automatically be set 8961 Uri urir3g4 = insertGroupMembership(r3, g4); 8962 assertFalse(queryRawContactIsStarred(r1)); 8963 assertFalse(queryRawContactIsStarred(r2)); 8964 assertTrue(queryRawContactIsStarred(r3)); 8965 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8966 c = queryGroupMemberships(mAccountTwo); 8967 try { 8968 assertTrue(c.moveToNext()); 8969 assertEquals(g4, c.getLong(0)); 8970 assertEquals(r3, c.getLong(1)); 8971 assertFalse(c.moveToNext()); 8972 } finally { 8973 c.close(); 8974 } 8975 8976 // remove r3 from the favorites group 8977 mResolver.delete(urir3g4, null, null); 8978 // r3's star should automatically be cleared 8979 assertFalse(queryRawContactIsStarred(r1)); 8980 assertFalse(queryRawContactIsStarred(r2)); 8981 assertFalse(queryRawContactIsStarred(r3)); 8982 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 8983 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 8984 } 8985 8986 @Test testReadOnlyRawContact()8987 public void testReadOnlyRawContact() { 8988 long rawContactId = RawContactUtil.createRawContact(mResolver); 8989 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 8990 storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first"); 8991 storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1); 8992 8993 storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second"); 8994 assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first"); 8995 8996 Uri syncAdapterUri = rawContactUri.buildUpon() 8997 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1") 8998 .build(); 8999 storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third"); 9000 assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third"); 9001 } 9002 9003 @Test testReadOnlyDataRow()9004 public void testReadOnlyDataRow() { 9005 long rawContactId = RawContactUtil.createRawContact(mResolver); 9006 Uri emailUri = insertEmail(rawContactId, "email"); 9007 Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111"); 9008 9009 storeValue(emailUri, Data.IS_READ_ONLY, "1"); 9010 storeValue(emailUri, Email.ADDRESS, "changed"); 9011 storeValue(phoneUri, Phone.NUMBER, "555-2222"); 9012 assertStoredValue(emailUri, Email.ADDRESS, "email"); 9013 assertStoredValue(phoneUri, Phone.NUMBER, "555-2222"); 9014 9015 Uri syncAdapterUri = emailUri.buildUpon() 9016 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1") 9017 .build(); 9018 storeValue(syncAdapterUri, Email.ADDRESS, "changed"); 9019 assertStoredValue(emailUri, Email.ADDRESS, "changed"); 9020 } 9021 9022 @Test testContactWithReadOnlyRawContact()9023 public void testContactWithReadOnlyRawContact() { 9024 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 9025 Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 9026 storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first"); 9027 9028 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 9029 Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); 9030 storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second"); 9031 storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1); 9032 9033 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 9034 rawContactId1, rawContactId2); 9035 9036 long contactId = queryContactId(rawContactId1); 9037 9038 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 9039 storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt"); 9040 assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt"); 9041 assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt"); 9042 assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second"); 9043 } 9044 9045 @Test testNameParsingQuery()9046 public void testNameParsingQuery() { 9047 Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name") 9048 .appendQueryParameter(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.").build(); 9049 Cursor cursor = mResolver.query(uri, null, null, null, null); 9050 ContentValues values = new ContentValues(); 9051 values.put(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr."); 9052 values.put(StructuredName.PREFIX, "Mr."); 9053 values.put(StructuredName.GIVEN_NAME, "John"); 9054 values.put(StructuredName.MIDDLE_NAME, "Q."); 9055 values.put(StructuredName.FAMILY_NAME, "Doe"); 9056 values.put(StructuredName.SUFFIX, "Jr."); 9057 values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN); 9058 assertTrue(cursor.moveToFirst()); 9059 assertCursorValues(cursor, values); 9060 cursor.close(); 9061 } 9062 9063 @Test testNameConcatenationQuery()9064 public void testNameConcatenationQuery() { 9065 Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name") 9066 .appendQueryParameter(StructuredName.PREFIX, "Mr") 9067 .appendQueryParameter(StructuredName.GIVEN_NAME, "John") 9068 .appendQueryParameter(StructuredName.MIDDLE_NAME, "Q.") 9069 .appendQueryParameter(StructuredName.FAMILY_NAME, "Doe") 9070 .appendQueryParameter(StructuredName.SUFFIX, "Jr.") 9071 .build(); 9072 Cursor cursor = mResolver.query(uri, null, null, null, null); 9073 ContentValues values = new ContentValues(); 9074 values.put(StructuredName.DISPLAY_NAME, "Mr John Q. Doe, Jr."); 9075 values.put(StructuredName.PREFIX, "Mr"); 9076 values.put(StructuredName.GIVEN_NAME, "John"); 9077 values.put(StructuredName.MIDDLE_NAME, "Q."); 9078 values.put(StructuredName.FAMILY_NAME, "Doe"); 9079 values.put(StructuredName.SUFFIX, "Jr."); 9080 values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN); 9081 assertTrue(cursor.moveToFirst()); 9082 assertCursorValues(cursor, values); 9083 cursor.close(); 9084 } 9085 9086 @Test testBuildSingleRowResult()9087 public void testBuildSingleRowResult() { 9088 checkBuildSingleRowResult( 9089 new String[] {"b"}, 9090 new String[] {"a", "b"}, 9091 new Integer[] {1, 2}, 9092 new Integer[] {2} 9093 ); 9094 9095 checkBuildSingleRowResult( 9096 new String[] {"b", "a", "b"}, 9097 new String[] {"a", "b"}, 9098 new Integer[] {1, 2}, 9099 new Integer[] {2, 1, 2} 9100 ); 9101 9102 checkBuildSingleRowResult( 9103 null, // all columns 9104 new String[] {"a", "b"}, 9105 new Integer[] {1, 2}, 9106 new Integer[] {1, 2} 9107 ); 9108 9109 try { 9110 // Access non-existent column 9111 ContactsProvider2.buildSingleRowResult(new String[] {"a"}, new String[] {"b"}, 9112 new Object[] {1}); 9113 fail(); 9114 } catch (IllegalArgumentException expected) { 9115 } 9116 } 9117 checkBuildSingleRowResult(String[] projection, String[] availableColumns, Object[] data, Integer[] expectedValues)9118 private void checkBuildSingleRowResult(String[] projection, String[] availableColumns, 9119 Object[] data, Integer[] expectedValues) { 9120 final Cursor c = ContactsProvider2.buildSingleRowResult(projection, availableColumns, data); 9121 try { 9122 assertTrue(c.moveToFirst()); 9123 assertEquals(1, c.getCount()); 9124 assertEquals(expectedValues.length, c.getColumnCount()); 9125 9126 for (int i = 0; i < expectedValues.length; i++) { 9127 assertEquals("column " + i, expectedValues[i], (Integer) c.getInt(i)); 9128 } 9129 } finally { 9130 c.close(); 9131 } 9132 } 9133 9134 @Test testDataUsageFeedbackAndDelete()9135 public void testDataUsageFeedbackAndDelete() { 9136 9137 sMockClock.install(); 9138 sMockClock.setCurrentTimeMillis(System.currentTimeMillis()); 9139 final long startTime = sMockClock.currentTimeMillis(); 9140 9141 final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a"); 9142 final long did1a = ContentUris.parseId(insertEmail(rid1, "[email protected]")); 9143 final long did1b = ContentUris.parseId(insertEmail(rid1, "[email protected]")); 9144 final long did1p = ContentUris.parseId(insertPhoneNumber(rid1, "555-555-5555")); 9145 9146 final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "contact", "b"); 9147 final long did2a = ContentUris.parseId(insertEmail(rid2, "[email protected]")); 9148 final long did2p = ContentUris.parseId(insertPhoneNumber(rid2, "555-555-5556")); 9149 9150 // Aggregate 1 and 2 9151 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rid1, rid2); 9152 9153 final long rid3 = RawContactUtil.createRawContactWithName(mResolver, "contact", "c"); 9154 final long did3a = ContentUris.parseId(insertEmail(rid3, "[email protected]")); 9155 final long did3p = ContentUris.parseId(insertPhoneNumber(rid3, "555-3333")); 9156 9157 final long rid4 = RawContactUtil.createRawContactWithName(mResolver, "contact", "d"); 9158 final long did4p = ContentUris.parseId(insertPhoneNumber(rid4, "555-4444")); 9159 9160 final long cid1 = queryContactId(rid1); 9161 final long cid3 = queryContactId(rid3); 9162 final long cid4 = queryContactId(rid4); 9163 9164 // Make sure 1+2, 3 and 4 aren't aggregated 9165 MoreAsserts.assertNotEqual(cid1, cid3); 9166 MoreAsserts.assertNotEqual(cid1, cid4); 9167 MoreAsserts.assertNotEqual(cid3, cid4); 9168 9169 // time = startTime 9170 9171 // First, there's no frequent. (We use strequent here only because frequent is hidden 9172 // and may be removed someday.) 9173 assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null); 9174 9175 // Test 1. touch data 1a 9176 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a); 9177 9178 // (We no longer populate frequent, so 0.) 9179 assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null); 9180 9181 sMockClock.advanceDay(); 9182 9183 // Test 2. touch data 1a, 2a and 3a 9184 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a, did2a, did3a); 9185 9186 // (We no longer populate frequent, so 0.) 9187 assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null); 9188 9189 sMockClock.advanceDay(); 9190 9191 // Test 2. touch data 2p (call) 9192 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did2p); 9193 9194 // (We no longer populate frequent, so 0.) 9195 assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null); 9196 9197 sMockClock.advanceDay(); 9198 9199 // Test 3. touch data 2p and 3p (short text) 9200 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, did2p, did3p); 9201 9202 // Let's check the tables. 9203 9204 // Fist, check the data_usage_stat table, which has no public URI. 9205 assertStoredValuesDb("SELECT " + DataUsageStatColumns.DATA_ID + 9206 "," + DataUsageStatColumns.USAGE_TYPE_INT + 9207 "," + DataUsageStatColumns.RAW_TIMES_USED + 9208 "," + DataUsageStatColumns.RAW_LAST_TIME_USED + 9209 " FROM " + Tables.DATA_USAGE_STAT, null 9210 ); 9211 9212 // Next, check the raw_contacts table 9213 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9214 cv(RawContacts._ID, rid1, 9215 RawContacts.TIMES_CONTACTED, 0, 9216 RawContacts.LAST_TIME_CONTACTED, 0 9217 ), 9218 cv(RawContacts._ID, rid2, 9219 RawContacts.TIMES_CONTACTED, 0, 9220 RawContacts.LAST_TIME_CONTACTED, 0 9221 ), 9222 cv(RawContacts._ID, rid3, 9223 RawContacts.TIMES_CONTACTED, 0, 9224 RawContacts.LAST_TIME_CONTACTED, 0 9225 ), 9226 cv(RawContacts._ID, rid4, 9227 RawContacts.TIMES_CONTACTED, 0, 9228 RawContacts.LAST_TIME_CONTACTED, 0 9229 ) 9230 ); 9231 9232 // Lastly, check the contacts table. 9233 9234 // Note contact1.TIMES_CONTACTED = 4, even though raw_contact1.TIMES_CONTACTED + 9235 // raw_contact1.TIMES_CONTACTED = 5, because in test 2, data 1a and data 2a were touched 9236 // at once. 9237 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9238 cv(Contacts._ID, cid1, 9239 Contacts.TIMES_CONTACTED, 0, 9240 Contacts.LAST_TIME_CONTACTED, 0 9241 ), 9242 cv(Contacts._ID, cid3, 9243 Contacts.TIMES_CONTACTED, 0, 9244 Contacts.LAST_TIME_CONTACTED, 0 9245 ), 9246 cv(Contacts._ID, cid4, 9247 Contacts.TIMES_CONTACTED, 0, 9248 Contacts.LAST_TIME_CONTACTED, 0 9249 ) 9250 ); 9251 9252 // Let's test the delete too. 9253 assertTrue(mResolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0); 9254 9255 // Now there's no frequent. 9256 assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null); 9257 9258 // No rows in the stats table. 9259 assertStoredValuesDb("SELECT " + DataUsageStatColumns.DATA_ID + 9260 " FROM " + Tables.DATA_USAGE_STAT, null, 9261 new ContentValues[0]); 9262 9263 // The following values should all be 0 or null. 9264 assertRowCount(0, Contacts.CONTENT_URI, Contacts.TIMES_CONTACTED + ">0", null); 9265 assertRowCount(0, Contacts.CONTENT_URI, Contacts.LAST_TIME_CONTACTED + ">0", null); 9266 assertRowCount(0, RawContacts.CONTENT_URI, RawContacts.TIMES_CONTACTED + ">0", null); 9267 assertRowCount(0, RawContacts.CONTENT_URI, RawContacts.LAST_TIME_CONTACTED + ">0", null); 9268 9269 // Calling it when there's no usage stats will still return a positive value. 9270 assertTrue(mResolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0); 9271 } 9272 9273 /******************************************************* 9274 * Delta api tests. 9275 */ 9276 @Test testContactDelete_hasDeleteLog()9277 public void testContactDelete_hasDeleteLog() { 9278 sMockClock.install(); 9279 long start = sMockClock.currentTimeMillis(); 9280 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(); 9281 DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, start); 9282 9283 // Clean up. Must also remove raw contact. 9284 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9285 } 9286 9287 @Test testContactDelete_marksRawContactsForDeletion()9288 public void testContactDelete_marksRawContactsForDeletion() { 9289 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount); 9290 9291 String[] projection = new String[]{ContactsContract.RawContacts.DIRTY, 9292 ContactsContract.RawContacts.DELETED}; 9293 String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId, 9294 projection); 9295 assertEquals("1", record[0]); 9296 assertEquals("1", record[1]); 9297 9298 // Clean up 9299 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9300 } 9301 9302 @Test testContactDelete_checkRawContactContactId()9303 public void testContactDelete_checkRawContactContactId() { 9304 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount); 9305 9306 String[] projection = new String[]{ContactsContract.RawContacts.CONTACT_ID}; 9307 String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId, 9308 projection); 9309 assertNull(record[0]); 9310 9311 // Clean up 9312 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9313 } 9314 9315 @Test testContactUpdate_metadataChange()9316 public void testContactUpdate_metadataChange() { 9317 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9318 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, ids.mRawContactId); 9319 assertDirty(rawContactUri, true); 9320 clearDirty(rawContactUri); 9321 9322 ContentValues values = new ContentValues(); 9323 values.put(Contacts.PINNED, 1); 9324 9325 ContactUtil.update(mResolver, ids.mContactId, values); 9326 assertDirty(rawContactUri, false); 9327 assertMetadataDirty(rawContactUri, false); 9328 assertNetworkNotified(false); 9329 } 9330 9331 @Test testContactUpdate_updatesContactUpdatedTimestamp()9332 public void testContactUpdate_updatesContactUpdatedTimestamp() { 9333 sMockClock.install(); 9334 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9335 9336 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9337 9338 ContentValues values = new ContentValues(); 9339 values.put(ContactsContract.Contacts.STARRED, 1); 9340 9341 sMockClock.advance(); 9342 ContactUtil.update(mResolver, ids.mContactId, values); 9343 9344 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9345 assertTrue(newTime > baseTime); 9346 9347 // Clean up 9348 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9349 } 9350 9351 // This implicitly tests the Contact create case. 9352 @Test testRawContactCreate_updatesContactUpdatedTimestamp()9353 public void testRawContactCreate_updatesContactUpdatedTimestamp() { 9354 long startTime = System.currentTimeMillis(); 9355 9356 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 9357 long lastUpdated = getContactLastUpdatedTimestampByRawContactId(mResolver, rawContactId); 9358 9359 assertTrue(lastUpdated > startTime); 9360 9361 // Clean up 9362 RawContactUtil.delete(mResolver, rawContactId, true); 9363 } 9364 9365 @Test testRawContactUpdate_updatesContactUpdatedTimestamp()9366 public void testRawContactUpdate_updatesContactUpdatedTimestamp() { 9367 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9368 9369 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9370 9371 ContentValues values = new ContentValues(); 9372 values.put(ContactsContract.RawContacts.STARRED, 1); 9373 RawContactUtil.update(mResolver, ids.mRawContactId, values); 9374 9375 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9376 assertTrue(newTime > baseTime); 9377 9378 // Clean up 9379 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9380 } 9381 9382 @Test testRawContactPsuedoDelete_hasDeleteLogForContact()9383 public void testRawContactPsuedoDelete_hasDeleteLogForContact() { 9384 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9385 9386 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9387 9388 RawContactUtil.delete(mResolver, ids.mRawContactId, false); 9389 9390 DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, baseTime); 9391 9392 // clean up 9393 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9394 } 9395 9396 @Test testRawContactDelete_hasDeleteLogForContact()9397 public void testRawContactDelete_hasDeleteLogForContact() { 9398 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9399 9400 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9401 9402 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9403 9404 DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, baseTime); 9405 9406 // already clean 9407 } 9408 getContactLastUpdatedTimestampByRawContactId(ContentResolver resolver, long rawContactId)9409 private long getContactLastUpdatedTimestampByRawContactId(ContentResolver resolver, 9410 long rawContactId) { 9411 long contactId = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId); 9412 MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId); 9413 9414 return ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId); 9415 } 9416 9417 @Test testDataInsert_updatesContactLastUpdatedTimestamp()9418 public void testDataInsert_updatesContactLastUpdatedTimestamp() { 9419 sMockClock.install(); 9420 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9421 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9422 9423 sMockClock.advance(); 9424 insertPhoneNumberAndReturnDataId(ids.mRawContactId); 9425 9426 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9427 assertTrue(newTime > baseTime); 9428 9429 // Clean up 9430 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9431 } 9432 9433 @Test testDataDelete_updatesContactLastUpdatedTimestamp()9434 public void testDataDelete_updatesContactLastUpdatedTimestamp() { 9435 sMockClock.install(); 9436 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9437 9438 long dataId = insertPhoneNumberAndReturnDataId(ids.mRawContactId); 9439 9440 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9441 9442 sMockClock.advance(); 9443 DataUtil.delete(mResolver, dataId); 9444 9445 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9446 assertTrue(newTime > baseTime); 9447 9448 // Clean up 9449 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9450 } 9451 9452 @Test testDataUpdate_updatesContactLastUpdatedTimestamp()9453 public void testDataUpdate_updatesContactLastUpdatedTimestamp() { 9454 sMockClock.install(); 9455 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 9456 9457 long dataId = insertPhoneNumberAndReturnDataId(ids.mRawContactId); 9458 9459 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9460 9461 sMockClock.advance(); 9462 ContentValues values = new ContentValues(); 9463 values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "555-5555"); 9464 DataUtil.update(mResolver, dataId, values); 9465 9466 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 9467 assertTrue(newTime > baseTime); 9468 9469 // Clean up 9470 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 9471 } 9472 insertPhoneNumberAndReturnDataId(long rawContactId)9473 private long insertPhoneNumberAndReturnDataId(long rawContactId) { 9474 Uri uri = insertPhoneNumber(rawContactId, "1-800-GOOG-411"); 9475 return ContentUris.parseId(uri); 9476 } 9477 9478 @Test testDeletedContactsDelete_isUnsupported()9479 public void testDeletedContactsDelete_isUnsupported() { 9480 final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI; 9481 DatabaseAsserts.assertDeleteIsUnsupported(mResolver, URI); 9482 9483 Uri uri = ContentUris.withAppendedId(URI, 1L); 9484 DatabaseAsserts.assertDeleteIsUnsupported(mResolver, uri); 9485 } 9486 9487 @Test testDeletedContactsInsert_isUnsupported()9488 public void testDeletedContactsInsert_isUnsupported() { 9489 final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI; 9490 DatabaseAsserts.assertInsertIsUnsupported(mResolver, URI); 9491 } 9492 9493 9494 @Test testQueryDeletedContactsByContactId()9495 public void testQueryDeletedContactsByContactId() { 9496 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(); 9497 9498 MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, 9499 DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId)); 9500 } 9501 9502 @Test testQueryDeletedContactsAll()9503 public void testQueryDeletedContactsAll() { 9504 final int numDeletes = 10; 9505 9506 // Since we cannot clean out delete log from previous tests, we need to account for that 9507 // by querying for the count first. 9508 final long startCount = DeletedContactUtil.getCount(mResolver); 9509 9510 for (int i = 0; i < numDeletes; i++) { 9511 assertContactCreateDelete(); 9512 } 9513 9514 final long endCount = DeletedContactUtil.getCount(mResolver); 9515 9516 assertEquals(numDeletes, endCount - startCount); 9517 } 9518 9519 @Test testQueryDeletedContactsSinceTimestamp()9520 public void testQueryDeletedContactsSinceTimestamp() { 9521 sMockClock.install(); 9522 9523 // Before 9524 final HashSet<Long> beforeIds = new HashSet<Long>(); 9525 beforeIds.add(assertContactCreateDelete().mContactId); 9526 beforeIds.add(assertContactCreateDelete().mContactId); 9527 9528 final long start = sMockClock.currentTimeMillis(); 9529 9530 // After 9531 final HashSet<Long> afterIds = new HashSet<Long>(); 9532 afterIds.add(assertContactCreateDelete().mContactId); 9533 afterIds.add(assertContactCreateDelete().mContactId); 9534 afterIds.add(assertContactCreateDelete().mContactId); 9535 9536 final String[] projection = new String[]{ 9537 ContactsContract.DeletedContacts.CONTACT_ID, 9538 ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP 9539 }; 9540 final List<String[]> records = DeletedContactUtil.querySinceTimestamp(mResolver, projection, 9541 start); 9542 for (String[] record : records) { 9543 // Check ids to make sure we only have the ones that came after the time. 9544 final long contactId = Long.parseLong(record[0]); 9545 assertFalse(beforeIds.contains(contactId)); 9546 assertTrue(afterIds.contains(contactId)); 9547 9548 // Check times to make sure they came after 9549 assertTrue(Long.parseLong(record[1]) > start); 9550 } 9551 } 9552 9553 /** 9554 * Creates a contact in the local account. Assert it's not present in the delete log. 9555 * Delete it. And assert that the contact record is no longer present. 9556 * 9557 * @return The contact id and raw contact id that was created. 9558 */ assertContactCreateDelete()9559 private DatabaseAsserts.ContactIdPair assertContactCreateDelete() { 9560 return assertContactCreateDelete(null); 9561 } 9562 9563 /** 9564 * Creates a contact in the given account. Assert it's not present in the delete log. 9565 * Delete it. And assert that the contact record is no longer present. 9566 * @return The contact id and raw contact id that was created. 9567 */ assertContactCreateDelete(Account account)9568 private DatabaseAsserts.ContactIdPair assertContactCreateDelete(Account account) { 9569 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver, 9570 account); 9571 9572 assertEquals(CommonDatabaseUtils.NOT_FOUND, 9573 DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId)); 9574 9575 sMockClock.advance(); 9576 ContactUtil.delete(mResolver, ids.mContactId); 9577 9578 assertFalse(ContactUtil.recordExistsForContactId(mResolver, ids.mContactId)); 9579 9580 return ids; 9581 } 9582 9583 /** 9584 * End delta api tests. 9585 ******************************************************/ 9586 9587 /******************************************************* 9588 * Pinning support tests 9589 */ 9590 @Test testPinnedPositionsUpdate()9591 public void testPinnedPositionsUpdate() { 9592 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver); 9593 final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver); 9594 final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver); 9595 final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver); 9596 9597 final int unpinned = PinnedPositions.UNPINNED; 9598 9599 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9600 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9601 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9602 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9603 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0) 9604 ); 9605 9606 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9607 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, unpinned), 9608 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned), 9609 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned), 9610 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, unpinned) 9611 ); 9612 9613 final ArrayList<ContentProviderOperation> operations = 9614 new ArrayList<ContentProviderOperation>(); 9615 9616 operations.add(newPinningOperation(i1.mContactId, 1, true)); 9617 operations.add(newPinningOperation(i3.mContactId, 3, true)); 9618 operations.add(newPinningOperation(i4.mContactId, 2, false)); 9619 9620 CommonDatabaseUtils.applyBatch(mResolver, operations); 9621 9622 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9623 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1), 9624 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9625 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3, Contacts.STARRED, 1), 9626 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0) 9627 ); 9628 9629 // Make sure the values are propagated to raw contacts 9630 9631 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9632 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1), 9633 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned), 9634 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3), 9635 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 2) 9636 ); 9637 9638 operations.clear(); 9639 9640 // Now unpin the contact 9641 operations.add(newPinningOperation(i3.mContactId, unpinned, false)); 9642 9643 CommonDatabaseUtils.applyBatch(mResolver, operations); 9644 9645 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9646 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1), 9647 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9648 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0), 9649 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0) 9650 ); 9651 9652 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9653 cv(Contacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, RawContacts.STARRED, 1), 9654 cv(Contacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned, 9655 RawContacts.STARRED, 0), 9656 cv(Contacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned, 9657 RawContacts.STARRED, 0), 9658 cv(Contacts._ID, i4.mRawContactId, RawContacts.PINNED, 2, RawContacts.STARRED, 0) 9659 ); 9660 } 9661 9662 @Test testPinnedPositionsAfterJoinAndSplit()9663 public void testPinnedPositionsAfterJoinAndSplit() { 9664 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContactWithName( 9665 mResolver, "A", "Smith"); 9666 final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContactWithName( 9667 mResolver, "B", "Smith"); 9668 final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContactWithName( 9669 mResolver, "C", "Smith"); 9670 final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContactWithName( 9671 mResolver, "D", "Smith"); 9672 final DatabaseAsserts.ContactIdPair i5 = DatabaseAsserts.assertAndCreateContactWithName( 9673 mResolver, "E", "Smith"); 9674 final DatabaseAsserts.ContactIdPair i6 = DatabaseAsserts.assertAndCreateContactWithName( 9675 mResolver, "F", "Smith"); 9676 9677 final ArrayList<ContentProviderOperation> operations = 9678 new ArrayList<ContentProviderOperation>(); 9679 9680 operations.add(newPinningOperation(i1.mContactId, 1, true)); 9681 operations.add(newPinningOperation(i2.mContactId, 2, true)); 9682 operations.add(newPinningOperation(i3.mContactId, 3, true)); 9683 operations.add(newPinningOperation(i5.mContactId, 5, true)); 9684 operations.add(newPinningOperation(i6.mContactId, 6, true)); 9685 9686 CommonDatabaseUtils.applyBatch(mResolver, operations); 9687 9688 // aggregate raw contact 1 and 4 together. 9689 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i1.mRawContactId, 9690 i4.mRawContactId); 9691 9692 // If only one contact is pinned, the resulting contact should inherit the pinned position 9693 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9694 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1), 9695 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2), 9696 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3), 9697 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 5), 9698 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6) 9699 ); 9700 9701 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9702 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, 9703 RawContacts.STARRED, 1), 9704 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2, 9705 RawContacts.STARRED, 1), 9706 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3, 9707 RawContacts.STARRED, 1), 9708 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED, 9709 RawContacts.STARRED, 0), 9710 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5, 9711 RawContacts.STARRED, 1), 9712 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6, 9713 RawContacts.STARRED, 1) 9714 ); 9715 9716 // aggregate raw contact 2 and 3 together. 9717 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i2.mRawContactId, 9718 i3.mRawContactId); 9719 9720 // If both raw contacts are pinned, the resulting contact should inherit the lower 9721 // pinned position 9722 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9723 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1), 9724 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2), 9725 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 5), 9726 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6) 9727 ); 9728 9729 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9730 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1), 9731 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2), 9732 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3), 9733 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 9734 PinnedPositions.UNPINNED), 9735 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5), 9736 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6) 9737 ); 9738 9739 // split the aggregated raw contacts 9740 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, i1.mRawContactId, 9741 i4.mRawContactId); 9742 9743 // raw contacts should be unpinned after being split, but still starred 9744 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9745 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, 9746 RawContacts.STARRED, 1), 9747 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2, 9748 RawContacts.STARRED, 1), 9749 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3, 9750 RawContacts.STARRED, 1), 9751 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED, 9752 RawContacts.STARRED, 0), 9753 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5, 9754 RawContacts.STARRED, 1), 9755 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6, 9756 RawContacts.STARRED, 1) 9757 ); 9758 9759 // now demote contact 5 9760 operations.clear(); 9761 operations.add(newPinningOperation(i5.mContactId, PinnedPositions.DEMOTED, false)); 9762 CommonDatabaseUtils.applyBatch(mResolver, operations); 9763 9764 // Get new contact Ids for contacts composing of raw contacts 1 and 4 because they have 9765 // changed. 9766 final long cId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, i1.mRawContactId); 9767 final long cId4 = RawContactUtil.queryContactIdByRawContactId(mResolver, i4.mRawContactId); 9768 9769 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9770 cv(Contacts._ID, cId1, Contacts.PINNED, 1), 9771 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2), 9772 cv(Contacts._ID, cId4, Contacts.PINNED, PinnedPositions.UNPINNED), 9773 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, PinnedPositions.DEMOTED), 9774 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6) 9775 ); 9776 9777 // aggregate contacts 5 and 6 together 9778 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i5.mRawContactId, 9779 i6.mRawContactId); 9780 9781 // The resulting contact should have a pinned value of 6 9782 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9783 cv(Contacts._ID, cId1, Contacts.PINNED, 1), 9784 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2), 9785 cv(Contacts._ID, cId4, Contacts.PINNED, PinnedPositions.UNPINNED), 9786 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 6) 9787 ); 9788 } 9789 9790 @Test testDefaultAccountSet_throwException()9791 public void testDefaultAccountSet_throwException() { 9792 mActor.setAccounts(new Account[]{mAccount}); 9793 try { 9794 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9795 null, null); 9796 fail(); 9797 } catch (SecurityException expected) { 9798 } 9799 9800 mActor.addPermissions("android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"); 9801 try { 9802 Bundle bundle = new Bundle(); 9803 bundle.putString(Settings.ACCOUNT_NAME, "account1"); // no account type specified 9804 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9805 null, bundle); 9806 fail(); 9807 } catch (IllegalArgumentException expected) { 9808 } 9809 9810 try { 9811 Bundle bundle = new Bundle(); 9812 bundle.putString(Settings.ACCOUNT_NAME, "account1"); 9813 bundle.putString(Settings.ACCOUNT_TYPE, "account type1"); 9814 bundle.putString(Settings.DATA_SET, "c"); // data set should not be set. 9815 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9816 null, bundle); 9817 fail(); 9818 } catch (IllegalArgumentException expected) { 9819 } 9820 9821 try { 9822 Bundle bundle = new Bundle(); 9823 bundle.putString(Settings.ACCOUNT_NAME, "account2"); // invalid account 9824 bundle.putString(Settings.ACCOUNT_TYPE, "account type2"); 9825 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9826 null, bundle); 9827 fail(); 9828 } catch (IllegalArgumentException expected) { 9829 } 9830 } 9831 9832 @Test testDefaultAccountSetAndQuery()9833 public void testDefaultAccountSetAndQuery() { 9834 Bundle response = mResolver.call(ContactsContract.AUTHORITY_URI, 9835 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null); 9836 Account account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT); 9837 assertNull(account); 9838 9839 mActor.addPermissions("android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"); 9840 mActor.setAccounts(new Account[]{mAccount}); 9841 // Set ("account1", "account type1") account as the default account. 9842 Bundle bundle = new Bundle(); 9843 bundle.putString(Settings.ACCOUNT_NAME, "account1"); 9844 bundle.putString(Settings.ACCOUNT_TYPE, "account type1"); 9845 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9846 null, bundle); 9847 9848 response = mResolver.call(ContactsContract.AUTHORITY_URI, 9849 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null); 9850 account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT); 9851 assertEquals("account1", account.name); 9852 assertEquals("account type1", account.type); 9853 9854 // Set NULL account as default account. 9855 bundle = new Bundle(); 9856 mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD, 9857 null, bundle); 9858 9859 response = mResolver.call(ContactsContract.AUTHORITY_URI, 9860 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null); 9861 account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT); 9862 assertNull(account); 9863 } 9864 9865 @Test testPinnedPositionsDemoteIllegalArguments()9866 public void testPinnedPositionsDemoteIllegalArguments() { 9867 try { 9868 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, 9869 null, null); 9870 fail(); 9871 } catch (IllegalArgumentException expected) { 9872 } 9873 9874 try { 9875 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, 9876 "1.1", null); 9877 fail(); 9878 } catch (IllegalArgumentException expected) { 9879 } 9880 9881 try { 9882 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, 9883 "NotANumber", null); 9884 fail(); 9885 } catch (IllegalArgumentException expected) { 9886 } 9887 9888 // Valid contact ID that does not correspond to an actual contact is silently ignored 9889 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, "999", 9890 null); 9891 } 9892 9893 @Test testPinnedPositionsAfterDemoteAndUndemote()9894 public void testPinnedPositionsAfterDemoteAndUndemote() { 9895 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver); 9896 final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver); 9897 9898 // Pin contact 1 and demote contact 2 9899 final ArrayList<ContentProviderOperation> operations = 9900 new ArrayList<ContentProviderOperation>(); 9901 operations.add(newPinningOperation(i1.mContactId, 1, true)); 9902 operations.add(newPinningOperation(i2.mContactId, PinnedPositions.DEMOTED, false)); 9903 CommonDatabaseUtils.applyBatch(mResolver, operations); 9904 9905 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9906 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1), 9907 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.DEMOTED, 9908 Contacts.STARRED, 0) 9909 ); 9910 9911 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9912 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, 9913 RawContacts.STARRED, 1), 9914 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, PinnedPositions.DEMOTED, 9915 RawContacts.STARRED, 0) 9916 ); 9917 9918 // Now undemote both contacts 9919 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, 9920 String.valueOf(i1.mContactId), null); 9921 mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, 9922 String.valueOf(i2.mContactId), null); 9923 9924 9925 // Contact 1 remains pinned at 0, while contact 2 becomes unpinned 9926 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9927 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1), 9928 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED, 9929 Contacts.STARRED, 0) 9930 ); 9931 9932 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 9933 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, 9934 RawContacts.STARRED, 1), 9935 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED, 9936 RawContacts.STARRED, 0) 9937 ); 9938 } 9939 9940 /** 9941 * Verifies that any existing pinned contacts have their pinned positions incremented by one 9942 * after the upgrade step 9943 */ 9944 @Test testPinnedPositionsUpgradeTo906_PinnedContactsIncrementedByOne()9945 public void testPinnedPositionsUpgradeTo906_PinnedContactsIncrementedByOne() { 9946 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver); 9947 final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver); 9948 final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver); 9949 final ArrayList<ContentProviderOperation> operations = 9950 new ArrayList<ContentProviderOperation>(); 9951 operations.add(newPinningOperation(i1.mContactId, 0, true)); 9952 operations.add(newPinningOperation(i2.mContactId, 5, true)); 9953 operations.add(newPinningOperation(i3.mContactId, Integer.MAX_VALUE - 2, true)); 9954 CommonDatabaseUtils.applyBatch(mResolver, operations); 9955 9956 final ContactsDatabaseHelper helper = 9957 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper()); 9958 SQLiteDatabase db = helper.getWritableDatabase(); 9959 helper.upgradeToVersion906(db); 9960 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9961 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1), 9962 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 6), 9963 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, Integer.MAX_VALUE - 1) 9964 ); 9965 } 9966 9967 /** 9968 * Verifies that any unpinned contacts (or those with pinned position Integer.MAX_VALUE - 1) 9969 * have their pinned positions correctly set to 0 after the upgrade step. 9970 */ 9971 @Test testPinnedPositionsUpgradeTo906_UnpinnedValueCorrectlyUpdated()9972 public void testPinnedPositionsUpgradeTo906_UnpinnedValueCorrectlyUpdated() { 9973 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver); 9974 final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver); 9975 final ArrayList<ContentProviderOperation> operations = 9976 new ArrayList<ContentProviderOperation>(); 9977 operations.add(newPinningOperation(i1.mContactId, Integer.MAX_VALUE -1 , true)); 9978 operations.add(newPinningOperation(i2.mContactId, Integer.MAX_VALUE, true)); 9979 CommonDatabaseUtils.applyBatch(mResolver, operations); 9980 9981 final ContactsDatabaseHelper helper = 9982 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper()); 9983 SQLiteDatabase db = helper.getWritableDatabase(); 9984 helper.upgradeToVersion906(db); 9985 9986 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 9987 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 0), 9988 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 0) 9989 ); 9990 } 9991 9992 /** 9993 * Tests the functionality of the 9994 * {@link ContactsContract.PinnedPositions#pin(ContentResolver, long, int)} API. 9995 */ 9996 @Test testPinnedPositions_ContactsContractPinnedPositionsPin()9997 public void testPinnedPositions_ContactsContractPinnedPositionsPin() { 9998 final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver); 9999 10000 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 10001 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED) 10002 ); 10003 10004 ContactsContract.PinnedPositions.pin(mResolver, i1.mContactId, 5); 10005 10006 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 10007 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 5) 10008 ); 10009 10010 ContactsContract.PinnedPositions.pin(mResolver, i1.mContactId, PinnedPositions.UNPINNED); 10011 10012 assertStoredValuesWithProjection(Contacts.CONTENT_URI, 10013 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED) 10014 ); 10015 } 10016 newPinningOperation(long id, int pinned, boolean star)10017 private ContentProviderOperation newPinningOperation(long id, int pinned, boolean star) { 10018 final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(id)); 10019 final ContentValues values = new ContentValues(); 10020 values.put(Contacts.PINNED, pinned); 10021 values.put(Contacts.STARRED, star ? 1 : 0); 10022 return ContentProviderOperation.newUpdate(uri).withValues(values).build(); 10023 } 10024 10025 /** 10026 * End pinning support tests 10027 ******************************************************/ 10028 10029 @Test testAuthorization_authorize()10030 public void testAuthorization_authorize() throws Exception { 10031 // Setup 10032 ContentValues values = new ContentValues(); 10033 long id1 = createContact(values, "Noah", "Tever", "18004664411", 10034 "[email protected]", StatusUpdates.OFFLINE, 0, 0, 0, 0); 10035 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1); 10036 10037 // Execute: pre authorize the contact 10038 Uri authorizedUri = getPreAuthorizedUri(contactUri); 10039 10040 // Sanity check: URIs are different 10041 assertNotSame(authorizedUri, contactUri); 10042 10043 // Verify: the URI is pre authorized 10044 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 10045 assertTrue(cp.isValidPreAuthorizedUri(authorizedUri)); 10046 } 10047 10048 @Test testAuthorization_unauthorized()10049 public void testAuthorization_unauthorized() throws Exception { 10050 // Setup 10051 ContentValues values = new ContentValues(); 10052 long id1 = createContact(values, "Noah", "Tever", "18004664411", 10053 "[email protected]", StatusUpdates.OFFLINE, 0, 0, 0, 0); 10054 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1); 10055 10056 // Verify: the URI is *not* pre authorized 10057 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 10058 assertFalse(cp.isValidPreAuthorizedUri(contactUri)); 10059 } 10060 10061 @Test testAuthorization_invalidAuthorization()10062 public void testAuthorization_invalidAuthorization() throws Exception { 10063 // Setup 10064 ContentValues values = new ContentValues(); 10065 long id1 = createContact(values, "Noah", "Tever", "18004664411", 10066 "[email protected]", StatusUpdates.OFFLINE, 0, 0, 0, 0); 10067 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1); 10068 10069 // Execute: pre authorize the contact and then modify the resulting URI slightly 10070 Uri authorizedUri = getPreAuthorizedUri(contactUri); 10071 Uri almostAuthorizedUri = Uri.parse(authorizedUri.toString() + "2"); 10072 10073 // Verify: the URI is not pre authorized 10074 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 10075 assertFalse(cp.isValidPreAuthorizedUri(almostAuthorizedUri)); 10076 } 10077 10078 @Test testAuthorization_expired()10079 public void testAuthorization_expired() throws Exception { 10080 // Setup 10081 ContentValues values = new ContentValues(); 10082 long id1 = createContact(values, "Noah", "Tever", "18004664411", 10083 "[email protected]", StatusUpdates.OFFLINE, 0, 0, 0, 0); 10084 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1); 10085 sMockClock.install(); 10086 10087 // Execute: pre authorize the contact 10088 Uri authorizedUri = getPreAuthorizedUri(contactUri); 10089 sMockClock.setCurrentTimeMillis(sMockClock.currentTimeMillis() + 1000000); 10090 10091 // Verify: the authorization for the URI expired 10092 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 10093 assertFalse(cp.isValidPreAuthorizedUri(authorizedUri)); 10094 } 10095 10096 @Test testAuthorization_contactUpgrade()10097 public void testAuthorization_contactUpgrade() throws Exception { 10098 ContactsDatabaseHelper helper = 10099 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper()); 10100 SQLiteDatabase db = helper.getWritableDatabase(); 10101 10102 // Perform the unit tests against an upgraded version of the database, instead of a freshly 10103 // created version of the database. 10104 helper.upgradeToVersion1002(db); 10105 testAuthorization_authorize(); 10106 helper.upgradeToVersion1002(db); 10107 testAuthorization_expired(); 10108 helper.upgradeToVersion1002(db); 10109 testAuthorization_expired(); 10110 helper.upgradeToVersion1002(db); 10111 testAuthorization_invalidAuthorization(); 10112 } 10113 getPreAuthorizedUri(Uri uri)10114 private Uri getPreAuthorizedUri(Uri uri) { 10115 final Bundle uriBundle = new Bundle(); 10116 uriBundle.putParcelable(ContactsContract.Authorization.KEY_URI_TO_AUTHORIZE, uri); 10117 final Bundle authResponse = mResolver.call( 10118 ContactsContract.AUTHORITY_URI, 10119 ContactsContract.Authorization.AUTHORIZATION_METHOD, 10120 null, 10121 uriBundle); 10122 return (Uri) authResponse.getParcelable( 10123 ContactsContract.Authorization.KEY_AUTHORIZED_URI); 10124 } 10125 10126 /** 10127 * End Authorization Tests 10128 ******************************************************/ 10129 queryGroupMemberships(Account account)10130 private Cursor queryGroupMemberships(Account account) { 10131 Cursor c = mResolver.query(TestUtil.maybeAddAccountQueryParameters(Data.CONTENT_URI, 10132 account), 10133 new String[] {GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID}, 10134 Data.MIMETYPE + "=?", new String[] {GroupMembership.CONTENT_ITEM_TYPE}, 10135 GroupMembership.GROUP_SOURCE_ID); 10136 return c; 10137 } 10138 assertQueryParameter(String uriString, String parameter, String expectedValue)10139 private void assertQueryParameter(String uriString, String parameter, String expectedValue) { 10140 assertEquals(expectedValue, ContactsProvider2.getQueryParameter( 10141 Uri.parse(uriString), parameter)); 10142 } 10143 createContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)10144 private long createContact(ContentValues values, String firstName, String givenName, 10145 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 10146 long groupId, int chatMode) { 10147 return createContact(values, firstName, givenName, phoneNumber, email, presenceStatus, 10148 timesContacted, starred, groupId, chatMode, false); 10149 } 10150 createContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)10151 private long createContact(ContentValues values, String firstName, String givenName, 10152 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 10153 long groupId, int chatMode, boolean isUserProfile) { 10154 return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email, 10155 presenceStatus, timesContacted, starred, groupId, chatMode, isUserProfile)); 10156 } 10157 createRawContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)10158 private long createRawContact(ContentValues values, String firstName, String givenName, 10159 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 10160 long groupId, int chatMode) { 10161 long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus, 10162 timesContacted, starred, groupId, chatMode); 10163 DataUtil.insertStructuredName(mResolver, rawContactId, firstName, givenName); 10164 return rawContactId; 10165 } 10166 createRawContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)10167 private long createRawContact(ContentValues values, String firstName, String givenName, 10168 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 10169 long groupId, int chatMode, boolean isUserProfile) { 10170 long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus, 10171 timesContacted, starred, groupId, chatMode, isUserProfile); 10172 DataUtil.insertStructuredName(mResolver, rawContactId, firstName, givenName); 10173 return rawContactId; 10174 } 10175 createRawContact(ContentValues values, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)10176 private long createRawContact(ContentValues values, String phoneNumber, String email, 10177 int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) { 10178 return createRawContact(values, phoneNumber, email, presenceStatus, timesContacted, starred, 10179 groupId, chatMode, false); 10180 } 10181 createRawContact(ContentValues values, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)10182 private long createRawContact(ContentValues values, String phoneNumber, String email, 10183 int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, 10184 boolean isUserProfile) { 10185 values.put(RawContacts.STARRED, starred); 10186 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 10187 values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5"); 10188 values.put(RawContacts.TIMES_CONTACTED, timesContacted); 10189 10190 Uri rawContactUri; 10191 if (isUserProfile) { 10192 rawContactUri = insertProfileRawContact(values); 10193 } else { 10194 rawContactUri = insertRawContact(values); 10195 } 10196 10197 long rawContactId = ContentUris.parseId(rawContactUri); 10198 Uri photoUri = insertPhoto(rawContactId); 10199 long photoId = ContentUris.parseId(photoUri); 10200 values.put(Contacts.PHOTO_ID, photoId); 10201 if (!TextUtils.isEmpty(phoneNumber)) { 10202 insertPhoneNumber(rawContactId, phoneNumber); 10203 } 10204 if (!TextUtils.isEmpty(email)) { 10205 insertEmail(rawContactId, email); 10206 } 10207 10208 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking", 10209 chatMode, isUserProfile); 10210 10211 if (groupId != 0) { 10212 insertGroupMembership(rawContactId, groupId); 10213 } 10214 10215 return rawContactId; 10216 } 10217 10218 /** 10219 * Creates a raw contact with pre-set values under the user's profile. 10220 * @param profileValues Values to be used to create the entry (common values will be 10221 * automatically populated in createRawContact()). 10222 * @return the raw contact ID that was created. 10223 */ createBasicProfileContact(ContentValues profileValues)10224 private long createBasicProfileContact(ContentValues profileValues) { 10225 long profileRawContactId = createRawContact(profileValues, "Mia", "Prophyl", 10226 "18005554411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 10227 StatusUpdates.CAPABILITY_HAS_CAMERA, true); 10228 profileValues.put(Contacts.DISPLAY_NAME, "Mia Prophyl"); 10229 return profileRawContactId; 10230 } 10231 10232 /** 10233 * Creates a raw contact with pre-set values that is not under the user's profile. 10234 * @param nonProfileValues Values to be used to create the entry (common values will be 10235 * automatically populated in createRawContact()). 10236 * @return the raw contact ID that was created. 10237 */ createBasicNonProfileContact(ContentValues nonProfileValues)10238 private long createBasicNonProfileContact(ContentValues nonProfileValues) { 10239 long nonProfileRawContactId = createRawContact(nonProfileValues, "John", "Doe", 10240 "18004664411", "[email protected]", StatusUpdates.INVISIBLE, 4, 1, 0, 10241 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 10242 nonProfileValues.put(Contacts.DISPLAY_NAME, "John Doe"); 10243 return nonProfileRawContactId; 10244 } 10245 putDataValues(ContentValues values, long rawContactId)10246 private void putDataValues(ContentValues values, long rawContactId) { 10247 values.put(Data.RAW_CONTACT_ID, rawContactId); 10248 values.put(Data.MIMETYPE, "testmimetype"); 10249 values.put(Data.RES_PACKAGE, "oldpackage"); 10250 values.put(Data.IS_PRIMARY, 1); 10251 values.put(Data.IS_SUPER_PRIMARY, 1); 10252 values.put(Data.DATA1, "one"); 10253 values.put(Data.DATA2, "two"); 10254 values.put(Data.DATA3, "three"); 10255 values.put(Data.DATA4, "four"); 10256 values.put(Data.DATA5, "five"); 10257 values.put(Data.DATA6, "six"); 10258 values.put(Data.DATA7, "seven"); 10259 values.put(Data.DATA8, "eight"); 10260 values.put(Data.DATA9, "nine"); 10261 values.put(Data.DATA10, "ten"); 10262 values.put(Data.DATA11, "eleven"); 10263 values.put(Data.DATA12, "twelve"); 10264 values.put(Data.DATA13, "thirteen"); 10265 values.put(Data.DATA14, "fourteen"); 10266 values.put(Data.DATA15, "fifteen".getBytes()); 10267 values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE); 10268 values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "preferredcomponentname"); 10269 values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "preferredid"); 10270 values.put(Data.SYNC1, "sync1"); 10271 values.put(Data.SYNC2, "sync2"); 10272 values.put(Data.SYNC3, "sync3"); 10273 values.put(Data.SYNC4, "sync4"); 10274 } 10275 10276 /** 10277 * @param data1 email address or phone number 10278 * @param usageType One of {@link DataUsageFeedback#USAGE_TYPE} 10279 * @param values ContentValues for this feedback. Useful for incrementing 10280 * {Contacts#TIMES_CONTACTED} in the ContentValue. Can be null. 10281 */ sendFeedback(String data1, String usageType, ContentValues values)10282 private void sendFeedback(String data1, String usageType, ContentValues values) { 10283 final long dataId = getStoredLongValue(Data.CONTENT_URI, 10284 Data.DATA1 + "=?", new String[] { data1 }, Data._ID); 10285 assertEquals(0, updateDataUsageFeedback(usageType, dataId)); 10286 if (values != null && values.containsKey(Contacts.TIMES_CONTACTED)) { 10287 values.put(Contacts.TIMES_CONTACTED, values.getAsInteger(Contacts.TIMES_CONTACTED) + 1); 10288 } 10289 } 10290 updateDataUsageFeedback(String usageType, Uri resultUri)10291 private void updateDataUsageFeedback(String usageType, Uri resultUri) { 10292 final long id = ContentUris.parseId(resultUri); 10293 final boolean successful = updateDataUsageFeedback(usageType, id) > 0; 10294 assertFalse(successful); // shouldn't succeed 10295 } 10296 updateDataUsageFeedback(String usageType, long... ids)10297 private int updateDataUsageFeedback(String usageType, long... ids) { 10298 final StringBuilder idList = new StringBuilder(); 10299 for (long id : ids) { 10300 if (idList.length() > 0) idList.append(","); 10301 idList.append(id); 10302 } 10303 return mResolver.update(DataUsageFeedback.FEEDBACK_URI.buildUpon() 10304 .appendPath(idList.toString()) 10305 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType) 10306 .build(), new ContentValues(), null, null); 10307 } 10308 hasChineseCollator()10309 private boolean hasChineseCollator() { 10310 final Locale locale[] = Collator.getAvailableLocales(); 10311 for (int i = 0; i < locale.length; i++) { 10312 if (locale[i].equals(Locale.CHINA)) { 10313 return true; 10314 } 10315 } 10316 return false; 10317 } 10318 hasJapaneseCollator()10319 private boolean hasJapaneseCollator() { 10320 final Locale locale[] = Collator.getAvailableLocales(); 10321 for (int i = 0; i < locale.length; i++) { 10322 if (locale[i].equals(Locale.JAPAN)) { 10323 return true; 10324 } 10325 } 10326 return false; 10327 } 10328 hasGermanCollator()10329 private boolean hasGermanCollator() { 10330 final Locale locale[] = Collator.getAvailableLocales(); 10331 for (int i = 0; i < locale.length; i++) { 10332 if (locale[i].equals(Locale.GERMANY)) { 10333 return true; 10334 } 10335 } 10336 return false; 10337 } 10338 } 10339