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