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 com.android.internal.util.ArrayUtils; 20 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; 21 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns; 22 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; 23 import com.android.providers.contacts.tests.R; 24 import com.google.android.collect.Lists; 25 26 import android.accounts.Account; 27 import android.content.ContentProviderOperation; 28 import android.content.ContentProviderResult; 29 import android.content.ContentUris; 30 import android.content.ContentValues; 31 import android.content.Entity; 32 import android.content.EntityIterator; 33 import android.content.res.AssetFileDescriptor; 34 import android.database.Cursor; 35 import android.net.Uri; 36 import android.os.AsyncTask; 37 import android.provider.ContactsContract; 38 import android.provider.ContactsContract.AggregationExceptions; 39 import android.provider.ContactsContract.CommonDataKinds.Email; 40 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 41 import android.provider.ContactsContract.CommonDataKinds.Im; 42 import android.provider.ContactsContract.CommonDataKinds.Organization; 43 import android.provider.ContactsContract.CommonDataKinds.Phone; 44 import android.provider.ContactsContract.CommonDataKinds.Photo; 45 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 46 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 47 import android.provider.ContactsContract.ContactCounts; 48 import android.provider.ContactsContract.Contacts; 49 import android.provider.ContactsContract.Data; 50 import android.provider.ContactsContract.DataUsageFeedback; 51 import android.provider.ContactsContract.Directory; 52 import android.provider.ContactsContract.DisplayNameSources; 53 import android.provider.ContactsContract.DisplayPhoto; 54 import android.provider.ContactsContract.FullNameStyle; 55 import android.provider.ContactsContract.Groups; 56 import android.provider.ContactsContract.PhoneLookup; 57 import android.provider.ContactsContract.PhoneticNameStyle; 58 import android.provider.ContactsContract.Profile; 59 import android.provider.ContactsContract.ProviderStatus; 60 import android.provider.ContactsContract.RawContacts; 61 import android.provider.ContactsContract.RawContactsEntity; 62 import android.provider.ContactsContract.SearchSnippetColumns; 63 import android.provider.ContactsContract.Settings; 64 import android.provider.ContactsContract.StatusUpdates; 65 import android.provider.ContactsContract.StreamItemPhotos; 66 import android.provider.ContactsContract.StreamItems; 67 import android.provider.OpenableColumns; 68 import android.test.MoreAsserts; 69 import android.test.suitebuilder.annotation.LargeTest; 70 import android.text.TextUtils; 71 72 import java.io.FileInputStream; 73 import java.io.IOException; 74 import java.io.InputStream; 75 import java.io.OutputStream; 76 import java.text.Collator; 77 import java.util.ArrayList; 78 import java.util.Arrays; 79 import java.util.List; 80 import java.util.Locale; 81 82 /** 83 * Unit tests for {@link ContactsProvider2}. 84 * 85 * Run the test like this: 86 * <code> 87 adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \ 88 com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 89 * </code> 90 */ 91 @LargeTest 92 public class ContactsProvider2Test extends BaseContactsProvider2Test { 93 94 private static final Account ACCOUNT_1 = new Account("account_name_1", "account_type_1"); 95 private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2"); 96 97 public void testContactsProjection() { 98 assertProjection(Contacts.CONTENT_URI, new String[]{ 99 Contacts._ID, 100 Contacts.DISPLAY_NAME_PRIMARY, 101 Contacts.DISPLAY_NAME_ALTERNATIVE, 102 Contacts.DISPLAY_NAME_SOURCE, 103 Contacts.PHONETIC_NAME, 104 Contacts.PHONETIC_NAME_STYLE, 105 Contacts.SORT_KEY_PRIMARY, 106 Contacts.SORT_KEY_ALTERNATIVE, 107 Contacts.LAST_TIME_CONTACTED, 108 Contacts.TIMES_CONTACTED, 109 Contacts.STARRED, 110 Contacts.IN_VISIBLE_GROUP, 111 Contacts.PHOTO_ID, 112 Contacts.PHOTO_FILE_ID, 113 Contacts.PHOTO_URI, 114 Contacts.PHOTO_THUMBNAIL_URI, 115 Contacts.CUSTOM_RINGTONE, 116 Contacts.HAS_PHONE_NUMBER, 117 Contacts.SEND_TO_VOICEMAIL, 118 Contacts.IS_USER_PROFILE, 119 Contacts.LOOKUP_KEY, 120 Contacts.NAME_RAW_CONTACT_ID, 121 Contacts.CONTACT_PRESENCE, 122 Contacts.CONTACT_CHAT_CAPABILITY, 123 Contacts.CONTACT_STATUS, 124 Contacts.CONTACT_STATUS_TIMESTAMP, 125 Contacts.CONTACT_STATUS_RES_PACKAGE, 126 Contacts.CONTACT_STATUS_LABEL, 127 Contacts.CONTACT_STATUS_ICON, 128 }); 129 } 130 131 public void testContactsWithSnippetProjection() { 132 assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(), 133 new String[]{ 134 Contacts._ID, 135 Contacts.DISPLAY_NAME_PRIMARY, 136 Contacts.DISPLAY_NAME_ALTERNATIVE, 137 Contacts.DISPLAY_NAME_SOURCE, 138 Contacts.PHONETIC_NAME, 139 Contacts.PHONETIC_NAME_STYLE, 140 Contacts.SORT_KEY_PRIMARY, 141 Contacts.SORT_KEY_ALTERNATIVE, 142 Contacts.LAST_TIME_CONTACTED, 143 Contacts.TIMES_CONTACTED, 144 Contacts.STARRED, 145 Contacts.IN_VISIBLE_GROUP, 146 Contacts.PHOTO_ID, 147 Contacts.PHOTO_FILE_ID, 148 Contacts.PHOTO_URI, 149 Contacts.PHOTO_THUMBNAIL_URI, 150 Contacts.CUSTOM_RINGTONE, 151 Contacts.HAS_PHONE_NUMBER, 152 Contacts.SEND_TO_VOICEMAIL, 153 Contacts.IS_USER_PROFILE, 154 Contacts.LOOKUP_KEY, 155 Contacts.NAME_RAW_CONTACT_ID, 156 Contacts.CONTACT_PRESENCE, 157 Contacts.CONTACT_CHAT_CAPABILITY, 158 Contacts.CONTACT_STATUS, 159 Contacts.CONTACT_STATUS_TIMESTAMP, 160 Contacts.CONTACT_STATUS_RES_PACKAGE, 161 Contacts.CONTACT_STATUS_LABEL, 162 Contacts.CONTACT_STATUS_ICON, 163 164 SearchSnippetColumns.SNIPPET, 165 }); 166 } 167 168 public void testRawContactsProjection() { 169 assertProjection(RawContacts.CONTENT_URI, new String[]{ 170 RawContacts._ID, 171 RawContacts.CONTACT_ID, 172 RawContacts.ACCOUNT_NAME, 173 RawContacts.ACCOUNT_TYPE, 174 RawContacts.DATA_SET, 175 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 176 RawContacts.SOURCE_ID, 177 RawContacts.VERSION, 178 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 179 RawContacts.DIRTY, 180 RawContacts.DELETED, 181 RawContacts.DISPLAY_NAME_PRIMARY, 182 RawContacts.DISPLAY_NAME_ALTERNATIVE, 183 RawContacts.DISPLAY_NAME_SOURCE, 184 RawContacts.PHONETIC_NAME, 185 RawContacts.PHONETIC_NAME_STYLE, 186 RawContacts.NAME_VERIFIED, 187 RawContacts.SORT_KEY_PRIMARY, 188 RawContacts.SORT_KEY_ALTERNATIVE, 189 RawContacts.TIMES_CONTACTED, 190 RawContacts.LAST_TIME_CONTACTED, 191 RawContacts.CUSTOM_RINGTONE, 192 RawContacts.SEND_TO_VOICEMAIL, 193 RawContacts.STARRED, 194 RawContacts.AGGREGATION_MODE, 195 RawContacts.SYNC1, 196 RawContacts.SYNC2, 197 RawContacts.SYNC3, 198 RawContacts.SYNC4, 199 }); 200 } 201 202 public void testDataProjection() { 203 assertProjection(Data.CONTENT_URI, new String[]{ 204 Data._ID, 205 Data.RAW_CONTACT_ID, 206 Data.DATA_VERSION, 207 Data.IS_PRIMARY, 208 Data.IS_SUPER_PRIMARY, 209 Data.RES_PACKAGE, 210 Data.MIMETYPE, 211 Data.DATA1, 212 Data.DATA2, 213 Data.DATA3, 214 Data.DATA4, 215 Data.DATA5, 216 Data.DATA6, 217 Data.DATA7, 218 Data.DATA8, 219 Data.DATA9, 220 Data.DATA10, 221 Data.DATA11, 222 Data.DATA12, 223 Data.DATA13, 224 Data.DATA14, 225 Data.DATA15, 226 Data.SYNC1, 227 Data.SYNC2, 228 Data.SYNC3, 229 Data.SYNC4, 230 Data.CONTACT_ID, 231 Data.PRESENCE, 232 Data.CHAT_CAPABILITY, 233 Data.STATUS, 234 Data.STATUS_TIMESTAMP, 235 Data.STATUS_RES_PACKAGE, 236 Data.STATUS_LABEL, 237 Data.STATUS_ICON, 238 RawContacts.ACCOUNT_NAME, 239 RawContacts.ACCOUNT_TYPE, 240 RawContacts.DATA_SET, 241 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 242 RawContacts.SOURCE_ID, 243 RawContacts.VERSION, 244 RawContacts.DIRTY, 245 RawContacts.NAME_VERIFIED, 246 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 247 Contacts._ID, 248 Contacts.DISPLAY_NAME_PRIMARY, 249 Contacts.DISPLAY_NAME_ALTERNATIVE, 250 Contacts.DISPLAY_NAME_SOURCE, 251 Contacts.PHONETIC_NAME, 252 Contacts.PHONETIC_NAME_STYLE, 253 Contacts.SORT_KEY_PRIMARY, 254 Contacts.SORT_KEY_ALTERNATIVE, 255 Contacts.LAST_TIME_CONTACTED, 256 Contacts.TIMES_CONTACTED, 257 Contacts.STARRED, 258 Contacts.IN_VISIBLE_GROUP, 259 Contacts.PHOTO_ID, 260 Contacts.PHOTO_FILE_ID, 261 Contacts.PHOTO_URI, 262 Contacts.PHOTO_THUMBNAIL_URI, 263 Contacts.CUSTOM_RINGTONE, 264 Contacts.SEND_TO_VOICEMAIL, 265 Contacts.LOOKUP_KEY, 266 Contacts.NAME_RAW_CONTACT_ID, 267 Contacts.HAS_PHONE_NUMBER, 268 Contacts.CONTACT_PRESENCE, 269 Contacts.CONTACT_CHAT_CAPABILITY, 270 Contacts.CONTACT_STATUS, 271 Contacts.CONTACT_STATUS_TIMESTAMP, 272 Contacts.CONTACT_STATUS_RES_PACKAGE, 273 Contacts.CONTACT_STATUS_LABEL, 274 Contacts.CONTACT_STATUS_ICON, 275 GroupMembership.GROUP_SOURCE_ID, 276 }); 277 } 278 279 public void testDistinctDataProjection() { 280 assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 281 new String[]{ 282 Data._ID, 283 Data.DATA_VERSION, 284 Data.IS_PRIMARY, 285 Data.IS_SUPER_PRIMARY, 286 Data.RES_PACKAGE, 287 Data.MIMETYPE, 288 Data.DATA1, 289 Data.DATA2, 290 Data.DATA3, 291 Data.DATA4, 292 Data.DATA5, 293 Data.DATA6, 294 Data.DATA7, 295 Data.DATA8, 296 Data.DATA9, 297 Data.DATA10, 298 Data.DATA11, 299 Data.DATA12, 300 Data.DATA13, 301 Data.DATA14, 302 Data.DATA15, 303 Data.SYNC1, 304 Data.SYNC2, 305 Data.SYNC3, 306 Data.SYNC4, 307 Data.CONTACT_ID, 308 Data.PRESENCE, 309 Data.CHAT_CAPABILITY, 310 Data.STATUS, 311 Data.STATUS_TIMESTAMP, 312 Data.STATUS_RES_PACKAGE, 313 Data.STATUS_LABEL, 314 Data.STATUS_ICON, 315 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 316 Contacts._ID, 317 Contacts.DISPLAY_NAME_PRIMARY, 318 Contacts.DISPLAY_NAME_ALTERNATIVE, 319 Contacts.DISPLAY_NAME_SOURCE, 320 Contacts.PHONETIC_NAME, 321 Contacts.PHONETIC_NAME_STYLE, 322 Contacts.SORT_KEY_PRIMARY, 323 Contacts.SORT_KEY_ALTERNATIVE, 324 Contacts.LAST_TIME_CONTACTED, 325 Contacts.TIMES_CONTACTED, 326 Contacts.STARRED, 327 Contacts.IN_VISIBLE_GROUP, 328 Contacts.PHOTO_ID, 329 Contacts.PHOTO_FILE_ID, 330 Contacts.PHOTO_URI, 331 Contacts.PHOTO_THUMBNAIL_URI, 332 Contacts.HAS_PHONE_NUMBER, 333 Contacts.CUSTOM_RINGTONE, 334 Contacts.SEND_TO_VOICEMAIL, 335 Contacts.LOOKUP_KEY, 336 Contacts.CONTACT_PRESENCE, 337 Contacts.CONTACT_CHAT_CAPABILITY, 338 Contacts.CONTACT_STATUS, 339 Contacts.CONTACT_STATUS_TIMESTAMP, 340 Contacts.CONTACT_STATUS_RES_PACKAGE, 341 Contacts.CONTACT_STATUS_LABEL, 342 Contacts.CONTACT_STATUS_ICON, 343 GroupMembership.GROUP_SOURCE_ID, 344 }); 345 } 346 347 public void testEntityProjection() { 348 assertProjection( 349 Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0), 350 Contacts.Entity.CONTENT_DIRECTORY), 351 new String[]{ 352 Contacts.Entity._ID, 353 Contacts.Entity.DATA_ID, 354 Contacts.Entity.RAW_CONTACT_ID, 355 Data.DATA_VERSION, 356 Data.IS_PRIMARY, 357 Data.IS_SUPER_PRIMARY, 358 Data.RES_PACKAGE, 359 Data.MIMETYPE, 360 Data.DATA1, 361 Data.DATA2, 362 Data.DATA3, 363 Data.DATA4, 364 Data.DATA5, 365 Data.DATA6, 366 Data.DATA7, 367 Data.DATA8, 368 Data.DATA9, 369 Data.DATA10, 370 Data.DATA11, 371 Data.DATA12, 372 Data.DATA13, 373 Data.DATA14, 374 Data.DATA15, 375 Data.SYNC1, 376 Data.SYNC2, 377 Data.SYNC3, 378 Data.SYNC4, 379 Data.CONTACT_ID, 380 Data.PRESENCE, 381 Data.CHAT_CAPABILITY, 382 Data.STATUS, 383 Data.STATUS_TIMESTAMP, 384 Data.STATUS_RES_PACKAGE, 385 Data.STATUS_LABEL, 386 Data.STATUS_ICON, 387 RawContacts.ACCOUNT_NAME, 388 RawContacts.ACCOUNT_TYPE, 389 RawContacts.DATA_SET, 390 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 391 RawContacts.SOURCE_ID, 392 RawContacts.VERSION, 393 RawContacts.DELETED, 394 RawContacts.DIRTY, 395 RawContacts.NAME_VERIFIED, 396 RawContacts.SYNC1, 397 RawContacts.SYNC2, 398 RawContacts.SYNC3, 399 RawContacts.SYNC4, 400 Contacts._ID, 401 Contacts.DISPLAY_NAME_PRIMARY, 402 Contacts.DISPLAY_NAME_ALTERNATIVE, 403 Contacts.DISPLAY_NAME_SOURCE, 404 Contacts.PHONETIC_NAME, 405 Contacts.PHONETIC_NAME_STYLE, 406 Contacts.SORT_KEY_PRIMARY, 407 Contacts.SORT_KEY_ALTERNATIVE, 408 Contacts.LAST_TIME_CONTACTED, 409 Contacts.TIMES_CONTACTED, 410 Contacts.STARRED, 411 Contacts.IN_VISIBLE_GROUP, 412 Contacts.PHOTO_ID, 413 Contacts.PHOTO_FILE_ID, 414 Contacts.PHOTO_URI, 415 Contacts.PHOTO_THUMBNAIL_URI, 416 Contacts.CUSTOM_RINGTONE, 417 Contacts.SEND_TO_VOICEMAIL, 418 Contacts.IS_USER_PROFILE, 419 Contacts.LOOKUP_KEY, 420 Contacts.NAME_RAW_CONTACT_ID, 421 Contacts.HAS_PHONE_NUMBER, 422 Contacts.CONTACT_PRESENCE, 423 Contacts.CONTACT_CHAT_CAPABILITY, 424 Contacts.CONTACT_STATUS, 425 Contacts.CONTACT_STATUS_TIMESTAMP, 426 Contacts.CONTACT_STATUS_RES_PACKAGE, 427 Contacts.CONTACT_STATUS_LABEL, 428 Contacts.CONTACT_STATUS_ICON, 429 GroupMembership.GROUP_SOURCE_ID, 430 }); 431 } 432 433 public void testRawEntityProjection() { 434 assertProjection(RawContactsEntity.CONTENT_URI, new String[]{ 435 RawContacts.Entity.DATA_ID, 436 RawContacts._ID, 437 RawContacts.CONTACT_ID, 438 RawContacts.ACCOUNT_NAME, 439 RawContacts.ACCOUNT_TYPE, 440 RawContacts.DATA_SET, 441 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 442 RawContacts.SOURCE_ID, 443 RawContacts.VERSION, 444 RawContacts.DIRTY, 445 RawContacts.NAME_VERIFIED, 446 RawContacts.DELETED, 447 RawContacts.SYNC1, 448 RawContacts.SYNC2, 449 RawContacts.SYNC3, 450 RawContacts.SYNC4, 451 RawContacts.STARRED, 452 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 453 Data.DATA_VERSION, 454 Data.IS_PRIMARY, 455 Data.IS_SUPER_PRIMARY, 456 Data.RES_PACKAGE, 457 Data.MIMETYPE, 458 Data.DATA1, 459 Data.DATA2, 460 Data.DATA3, 461 Data.DATA4, 462 Data.DATA5, 463 Data.DATA6, 464 Data.DATA7, 465 Data.DATA8, 466 Data.DATA9, 467 Data.DATA10, 468 Data.DATA11, 469 Data.DATA12, 470 Data.DATA13, 471 Data.DATA14, 472 Data.DATA15, 473 Data.SYNC1, 474 Data.SYNC2, 475 Data.SYNC3, 476 Data.SYNC4, 477 GroupMembership.GROUP_SOURCE_ID, 478 }); 479 } 480 481 public void testPhoneLookupProjection() { 482 assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 483 new String[]{ 484 PhoneLookup._ID, 485 PhoneLookup.LOOKUP_KEY, 486 PhoneLookup.DISPLAY_NAME, 487 PhoneLookup.LAST_TIME_CONTACTED, 488 PhoneLookup.TIMES_CONTACTED, 489 PhoneLookup.STARRED, 490 PhoneLookup.IN_VISIBLE_GROUP, 491 PhoneLookup.PHOTO_ID, 492 PhoneLookup.PHOTO_URI, 493 PhoneLookup.PHOTO_THUMBNAIL_URI, 494 PhoneLookup.CUSTOM_RINGTONE, 495 PhoneLookup.HAS_PHONE_NUMBER, 496 PhoneLookup.SEND_TO_VOICEMAIL, 497 PhoneLookup.NUMBER, 498 PhoneLookup.TYPE, 499 PhoneLookup.LABEL, 500 PhoneLookup.NORMALIZED_NUMBER, 501 }); 502 } 503 504 public void testGroupsProjection() { 505 assertProjection(Groups.CONTENT_URI, new String[]{ 506 Groups._ID, 507 Groups.ACCOUNT_NAME, 508 Groups.ACCOUNT_TYPE, 509 Groups.DATA_SET, 510 Groups.ACCOUNT_TYPE_AND_DATA_SET, 511 Groups.SOURCE_ID, 512 Groups.DIRTY, 513 Groups.VERSION, 514 Groups.RES_PACKAGE, 515 Groups.TITLE, 516 Groups.TITLE_RES, 517 Groups.GROUP_VISIBLE, 518 Groups.SYSTEM_ID, 519 Groups.DELETED, 520 Groups.NOTES, 521 Groups.SHOULD_SYNC, 522 Groups.FAVORITES, 523 Groups.AUTO_ADD, 524 Groups.GROUP_IS_READ_ONLY, 525 Groups.SYNC1, 526 Groups.SYNC2, 527 Groups.SYNC3, 528 Groups.SYNC4, 529 }); 530 } 531 532 public void testGroupsSummaryProjection() { 533 assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{ 534 Groups._ID, 535 Groups.ACCOUNT_NAME, 536 Groups.ACCOUNT_TYPE, 537 Groups.DATA_SET, 538 Groups.ACCOUNT_TYPE_AND_DATA_SET, 539 Groups.SOURCE_ID, 540 Groups.DIRTY, 541 Groups.VERSION, 542 Groups.RES_PACKAGE, 543 Groups.TITLE, 544 Groups.TITLE_RES, 545 Groups.GROUP_VISIBLE, 546 Groups.SYSTEM_ID, 547 Groups.DELETED, 548 Groups.NOTES, 549 Groups.SHOULD_SYNC, 550 Groups.FAVORITES, 551 Groups.AUTO_ADD, 552 Groups.GROUP_IS_READ_ONLY, 553 Groups.SYNC1, 554 Groups.SYNC2, 555 Groups.SYNC3, 556 Groups.SYNC4, 557 Groups.SUMMARY_COUNT, 558 Groups.SUMMARY_WITH_PHONES, 559 }); 560 } 561 562 public void testAggregateExceptionProjection() { 563 assertProjection(AggregationExceptions.CONTENT_URI, new String[]{ 564 AggregationExceptionColumns._ID, 565 AggregationExceptions.TYPE, 566 AggregationExceptions.RAW_CONTACT_ID1, 567 AggregationExceptions.RAW_CONTACT_ID2, 568 }); 569 } 570 571 public void testSettingsProjection() { 572 assertProjection(Settings.CONTENT_URI, new String[]{ 573 Settings.ACCOUNT_NAME, 574 Settings.ACCOUNT_TYPE, 575 Settings.DATA_SET, 576 Settings.UNGROUPED_VISIBLE, 577 Settings.SHOULD_SYNC, 578 Settings.ANY_UNSYNCED, 579 Settings.UNGROUPED_COUNT, 580 Settings.UNGROUPED_WITH_PHONES, 581 }); 582 } 583 584 public void testStatusUpdatesProjection() { 585 assertProjection(StatusUpdates.CONTENT_URI, new String[]{ 586 PresenceColumns.RAW_CONTACT_ID, 587 StatusUpdates.DATA_ID, 588 StatusUpdates.IM_ACCOUNT, 589 StatusUpdates.IM_HANDLE, 590 StatusUpdates.PROTOCOL, 591 StatusUpdates.CUSTOM_PROTOCOL, 592 StatusUpdates.PRESENCE, 593 StatusUpdates.CHAT_CAPABILITY, 594 StatusUpdates.STATUS, 595 StatusUpdates.STATUS_TIMESTAMP, 596 StatusUpdates.STATUS_RES_PACKAGE, 597 StatusUpdates.STATUS_ICON, 598 StatusUpdates.STATUS_LABEL, 599 }); 600 } 601 602 public void testDirectoryProjection() { 603 assertProjection(Directory.CONTENT_URI, new String[]{ 604 Directory._ID, 605 Directory.PACKAGE_NAME, 606 Directory.TYPE_RESOURCE_ID, 607 Directory.DISPLAY_NAME, 608 Directory.DIRECTORY_AUTHORITY, 609 Directory.ACCOUNT_TYPE, 610 Directory.ACCOUNT_NAME, 611 Directory.EXPORT_SUPPORT, 612 Directory.SHORTCUT_SUPPORT, 613 Directory.PHOTO_SUPPORT, 614 }); 615 } 616 617 public void testRawContactsInsert() { 618 ContentValues values = new ContentValues(); 619 620 values.put(RawContacts.ACCOUNT_NAME, "a"); 621 values.put(RawContacts.ACCOUNT_TYPE, "b"); 622 values.put(RawContacts.SOURCE_ID, "c"); 623 values.put(RawContacts.VERSION, 42); 624 values.put(RawContacts.DIRTY, 1); 625 values.put(RawContacts.DELETED, 1); 626 values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); 627 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 628 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 629 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 630 values.put(RawContacts.STARRED, 1); 631 values.put(RawContacts.SYNC1, "e"); 632 values.put(RawContacts.SYNC2, "f"); 633 values.put(RawContacts.SYNC3, "g"); 634 values.put(RawContacts.SYNC4, "h"); 635 636 Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values); 637 long rawContactId = ContentUris.parseId(rowUri); 638 639 assertStoredValues(rowUri, values); 640 assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId); 641 assertNetworkNotified(true); 642 } 643 644 public void testDataDirectoryWithLookupUri() { 645 ContentValues values = new ContentValues(); 646 647 long rawContactId = createRawContactWithName(); 648 insertPhoneNumber(rawContactId, "555-GOOG-411"); 649 insertEmail(rawContactId, "google (at) android.com"); 650 651 long contactId = queryContactId(rawContactId); 652 String lookupKey = queryLookupKey(contactId); 653 654 // Complete and valid lookup URI 655 Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 656 Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 657 658 assertDataRows(dataUri, values); 659 660 // Complete but stale lookup URI 661 lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 662 dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 663 assertDataRows(dataUri, values); 664 665 // Incomplete lookup URI (lookup key only, no contact ID) 666 dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, 667 lookupKey), Contacts.Data.CONTENT_DIRECTORY); 668 assertDataRows(dataUri, values); 669 } 670 671 private void assertDataRows(Uri dataUri, ContentValues values) { 672 Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID); 673 assertEquals(3, cursor.getCount()); 674 cursor.moveToFirst(); 675 values.put(Data.DATA1, "John Doe"); 676 assertCursorValues(cursor, values); 677 678 cursor.moveToNext(); 679 values.put(Data.DATA1, "555-GOOG-411"); 680 assertCursorValues(cursor, values); 681 682 cursor.moveToNext(); 683 values.put(Data.DATA1, "google (at) android.com"); 684 assertCursorValues(cursor, values); 685 686 cursor.close(); 687 } 688 689 public void testContactEntitiesWithIdBasedUri() { 690 ContentValues values = new ContentValues(); 691 Account account1 = new Account("act1", "actype1"); 692 Account account2 = new Account("act2", "actype2"); 693 694 long rawContactId1 = createRawContactWithName(account1); 695 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 696 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 697 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 698 699 long rawContactId2 = createRawContact(account2); 700 setAggregationException( 701 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 702 703 long contactId = queryContactId(rawContactId1); 704 705 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 706 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY); 707 708 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 709 } 710 711 public void testContactEntitiesWithLookupUri() { 712 ContentValues values = new ContentValues(); 713 Account account1 = new Account("act1", "actype1"); 714 Account account2 = new Account("act2", "actype2"); 715 716 long rawContactId1 = createRawContactWithName(account1); 717 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 718 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 719 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 720 721 long rawContactId2 = createRawContact(account2); 722 setAggregationException( 723 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 724 725 long contactId = queryContactId(rawContactId1); 726 String lookupKey = queryLookupKey(contactId); 727 728 // First try with a matching contact ID 729 Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 730 Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 731 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 732 733 // Now try with a contact ID mismatch 734 contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 735 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 736 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 737 738 // Now try without an ID altogether 739 contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey); 740 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 741 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 742 } 743 744 private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1, 745 long rawContactId2) { 746 ContentValues values = new ContentValues(); 747 748 Cursor cursor = mResolver.query(entityUri, null, null, null, 749 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID); 750 assertEquals(3, cursor.getCount()); 751 752 // First row - name 753 cursor.moveToFirst(); 754 values.put(Contacts.Entity.CONTACT_ID, contactId); 755 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 756 values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 757 values.put(Contacts.Entity.DATA1, "John Doe"); 758 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 759 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 760 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 761 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 762 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 763 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 764 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 765 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 766 values.putNull(Contacts.Entity.PRESENCE); 767 assertCursorValues(cursor, values); 768 769 // Second row - IM 770 cursor.moveToNext(); 771 values.put(Contacts.Entity.CONTACT_ID, contactId); 772 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 773 values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE); 774 values.put(Contacts.Entity.DATA1, "gtalk"); 775 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 776 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 777 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 778 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 779 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 780 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 781 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 782 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 783 values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE); 784 assertCursorValues(cursor, values); 785 786 // Third row - second raw contact, not data 787 cursor.moveToNext(); 788 values.put(Contacts.Entity.CONTACT_ID, contactId); 789 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2); 790 values.putNull(Contacts.Entity.MIMETYPE); 791 values.putNull(Contacts.Entity.DATA_ID); 792 values.putNull(Contacts.Entity.DATA1); 793 values.put(Contacts.Entity.ACCOUNT_NAME, "act2"); 794 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2"); 795 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 796 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 797 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 798 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 799 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 800 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 801 values.putNull(Contacts.Entity.PRESENCE); 802 assertCursorValues(cursor, values); 803 804 cursor.close(); 805 } 806 807 public void testDataInsert() { 808 long rawContactId = createRawContactWithName("John", "Doe"); 809 810 ContentValues values = new ContentValues(); 811 putDataValues(values, rawContactId); 812 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 813 long dataId = ContentUris.parseId(dataUri); 814 815 long contactId = queryContactId(rawContactId); 816 values.put(RawContacts.CONTACT_ID, contactId); 817 assertStoredValues(dataUri, values); 818 819 assertSelection(Data.CONTENT_URI, values, Data._ID, dataId); 820 821 // Access the same data through the directory under RawContacts 822 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 823 Uri rawContactDataUri = 824 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY); 825 assertSelection(rawContactDataUri, values, Data._ID, dataId); 826 827 // Access the same data through the directory under Contacts 828 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 829 Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY); 830 assertSelection(contactDataUri, values, Data._ID, dataId); 831 assertNetworkNotified(true); 832 } 833 834 public void testRawContactDataQuery() { 835 Account account1 = new Account("a", "b"); 836 Account account2 = new Account("c", "d"); 837 long rawContactId1 = createRawContact(account1); 838 Uri dataUri1 = insertStructuredName(rawContactId1, "John", "Doe"); 839 long rawContactId2 = createRawContact(account2); 840 Uri dataUri2 = insertStructuredName(rawContactId2, "Jane", "Doe"); 841 842 Uri uri1 = maybeAddAccountQueryParameters(dataUri1, account1); 843 Uri uri2 = maybeAddAccountQueryParameters(dataUri2, account2); 844 assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ; 845 assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ; 846 } 847 848 public void testPhonesQuery() { 849 850 ContentValues values = new ContentValues(); 851 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 852 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 853 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 854 values.put(RawContacts.TIMES_CONTACTED, 54321); 855 values.put(RawContacts.STARRED, 1); 856 857 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 858 long rawContactId = ContentUris.parseId(rawContactUri); 859 860 insertStructuredName(rawContactId, "Meghan", "Knox"); 861 Uri uri = insertPhoneNumber(rawContactId, "18004664411"); 862 long phoneId = ContentUris.parseId(uri); 863 864 865 long contactId = queryContactId(rawContactId); 866 values.clear(); 867 values.put(Data._ID, phoneId); 868 values.put(Data.RAW_CONTACT_ID, rawContactId); 869 values.put(RawContacts.CONTACT_ID, contactId); 870 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 871 values.put(Phone.NUMBER, "18004664411"); 872 values.put(Phone.TYPE, Phone.TYPE_HOME); 873 values.putNull(Phone.LABEL); 874 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 875 values.put(Contacts.CUSTOM_RINGTONE, "d"); 876 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 877 values.put(Contacts.LAST_TIME_CONTACTED, 12345); 878 values.put(Contacts.TIMES_CONTACTED, 54321); 879 values.put(Contacts.STARRED, 1); 880 881 assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values); 882 assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId); 883 } 884 885 public void testPhonesWithMergedContacts() { 886 long rawContactId1 = createRawContact(); 887 insertPhoneNumber(rawContactId1, "123456789", true); 888 889 long rawContactId2 = createRawContact(); 890 insertPhoneNumber(rawContactId2, "123456789", true); 891 892 ContentValues values1 = new ContentValues(); 893 values1.put(Contacts.DISPLAY_NAME, "123456789"); 894 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 895 values1.put(Phone.NUMBER, "123456789"); 896 897 // Two results should come, since they are separate entries anyway. 898 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 899 900 // Even with remove_duplicate_entries flag, we should return two results here, because 901 // they have different raw_contact_id-s. 902 final Uri dedupeUri = Phone.CONTENT_URI.buildUpon() 903 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 904 .build(); 905 assertStoredValues(dedupeUri, new ContentValues[] {values1, values1}); 906 907 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 908 rawContactId1, rawContactId2); 909 910 assertAggregated(rawContactId1, rawContactId2, "123456789"); 911 912 // Contact merge won't affect the default result of Phone Uri. 913 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 914 915 // We should detect duplicates when requested. 916 assertStoredValues(dedupeUri, values1); 917 } 918 919 public void testPhonesFilterQuery() { 920 long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1); 921 insertPhoneNumber(rawContactId1, "1-800-466-4411"); 922 923 long rawContactId2 = createRawContactWithName("Chilled", "Guacamole", ACCOUNT_2); 924 insertPhoneNumber(rawContactId2, "1-800-466-5432"); 925 insertPhoneNumber(rawContactId2, "0 (at) example.com", false, Phone.TYPE_PAGER); 926 insertPhoneNumber(rawContactId2, "1 (at) example.com", false, Phone.TYPE_PAGER); 927 928 Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale"); 929 ContentValues values = new ContentValues(); 930 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 931 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 932 values.put(Phone.NUMBER, "1-800-466-4411"); 933 values.put(Phone.TYPE, Phone.TYPE_HOME); 934 values.putNull(Phone.LABEL); 935 assertStoredValuesWithProjection(filterUri1, values); 936 937 Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411"); 938 assertStoredValues(filterUri2, values); 939 940 Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664"); 941 assertStoredValues(filterUri3, values); 942 943 Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada"); 944 assertEquals(0, getCount(filterUri4, null, null)); 945 946 Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*"); 947 assertEquals(0, getCount(filterUri5, null, null)); 948 949 ContentValues values1 = new ContentValues(); 950 values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 951 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 952 values1.put(Phone.NUMBER, "1-800-466-5432"); 953 values1.put(Phone.TYPE, Phone.TYPE_HOME); 954 values1.putNull(Phone.LABEL); 955 956 ContentValues values2 = new ContentValues(); 957 values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 958 values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 959 values2.put(Phone.NUMBER, "0 (at) example.com"); 960 values2.put(Phone.TYPE, Phone.TYPE_PAGER); 961 values2.putNull(Phone.LABEL); 962 963 ContentValues values3 = new ContentValues(); 964 values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 965 values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 966 values3.put(Phone.NUMBER, "1 (at) example.com"); 967 values3.put(Phone.TYPE, Phone.TYPE_PAGER); 968 values3.putNull(Phone.LABEL); 969 970 Uri filterUri6 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "Chilled"); 971 assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} ); 972 } 973 974 public void testPhoneLookup() { 975 ContentValues values = new ContentValues(); 976 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 977 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 978 979 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 980 long rawContactId = ContentUris.parseId(rawContactUri); 981 982 insertStructuredName(rawContactId, "Hot", "Tamale"); 983 insertPhoneNumber(rawContactId, "18004664411"); 984 985 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 986 987 values.clear(); 988 values.put(PhoneLookup._ID, queryContactId(rawContactId)); 989 values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 990 values.put(PhoneLookup.NUMBER, "18004664411"); 991 values.put(PhoneLookup.TYPE, Phone.TYPE_HOME); 992 values.putNull(PhoneLookup.LABEL); 993 values.put(PhoneLookup.CUSTOM_RINGTONE, "d"); 994 values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1); 995 assertStoredValues(lookupUri1, values); 996 997 // In the context that 8004664411 is a valid number, "4664411" as a 998 // call id should match to both "8004664411" and "+18004664411". 999 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411"); 1000 assertEquals(2, getCount(lookupUri2, null, null)); 1001 } 1002 1003 public void testPhoneLookupUseCases() { 1004 ContentValues values = new ContentValues(); 1005 Uri rawContactUri; 1006 long rawContactId; 1007 Uri lookupUri2; 1008 1009 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1010 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1011 1012 // International format in contacts 1013 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1014 rawContactId = ContentUris.parseId(rawContactUri); 1015 1016 insertStructuredName(rawContactId, "Hot", "Tamale"); 1017 insertPhoneNumber(rawContactId, "+1-650-861-0000"); 1018 1019 values.clear(); 1020 1021 // match with international format 1022 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000"); 1023 assertEquals(1, getCount(lookupUri2, null, null)); 1024 1025 // match with national format 1026 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000"); 1027 assertEquals(1, getCount(lookupUri2, null, null)); 1028 1029 // National format in contacts 1030 values.clear(); 1031 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1032 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1033 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1034 rawContactId = ContentUris.parseId(rawContactUri); 1035 1036 insertStructuredName(rawContactId, "Hot1", "Tamale"); 1037 insertPhoneNumber(rawContactId, "650-861-0001"); 1038 1039 values.clear(); 1040 1041 // match with international format 1042 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001"); 1043 assertEquals(2, getCount(lookupUri2, null, null)); 1044 1045 // match with national format 1046 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001"); 1047 assertEquals(2, getCount(lookupUri2, null, null)); 1048 1049 // Local format in contacts 1050 values.clear(); 1051 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1052 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1053 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1054 rawContactId = ContentUris.parseId(rawContactUri); 1055 1056 insertStructuredName(rawContactId, "Hot2", "Tamale"); 1057 insertPhoneNumber(rawContactId, "861-0002"); 1058 1059 values.clear(); 1060 1061 // match with international format 1062 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002"); 1063 assertEquals(1, getCount(lookupUri2, null, null)); 1064 1065 // match with national format 1066 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002"); 1067 assertEquals(1, getCount(lookupUri2, null, null)); 1068 } 1069 1070 public void testPhoneUpdate() { 1071 ContentValues values = new ContentValues(); 1072 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1073 long rawContactId = ContentUris.parseId(rawContactUri); 1074 1075 insertStructuredName(rawContactId, "Hot", "Tamale"); 1076 Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411"); 1077 1078 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 1079 assertStoredValue(lookupUri1, PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1080 1081 values.clear(); 1082 values.put(Phone.NUMBER, "18004664422"); 1083 mResolver.update(phoneUri, values, null, null); 1084 1085 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422"); 1086 assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1087 1088 // Setting number to null will remove the phone lookup record 1089 values.clear(); 1090 values.putNull(Phone.NUMBER); 1091 mResolver.update(phoneUri, values, null, null); 1092 1093 assertEquals(0, getCount(lookupUri2, null, null)); 1094 1095 // Let's restore that phone lookup record 1096 values.clear(); 1097 values.put(Phone.NUMBER, "18004664422"); 1098 mResolver.update(phoneUri, values, null, null); 1099 assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1100 assertNetworkNotified(true); 1101 } 1102 1103 public void testEmailsQuery() { 1104 ContentValues values = new ContentValues(); 1105 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1106 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1107 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 1108 values.put(RawContacts.TIMES_CONTACTED, 54321); 1109 values.put(RawContacts.STARRED, 1); 1110 1111 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1112 final long rawContactId = ContentUris.parseId(rawContactUri); 1113 1114 insertStructuredName(rawContactId, "Meghan", "Knox"); 1115 final Uri emailUri = insertEmail(rawContactId, "meghan (at) acme.com"); 1116 final long emailId = ContentUris.parseId(emailUri); 1117 1118 final long contactId = queryContactId(rawContactId); 1119 values.clear(); 1120 values.put(Data._ID, emailId); 1121 values.put(Data.RAW_CONTACT_ID, rawContactId); 1122 values.put(RawContacts.CONTACT_ID, contactId); 1123 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1124 values.put(Email.DATA, "meghan (at) acme.com"); 1125 values.put(Email.TYPE, Email.TYPE_HOME); 1126 values.putNull(Email.LABEL); 1127 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 1128 values.put(Contacts.CUSTOM_RINGTONE, "d"); 1129 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 1130 values.put(Contacts.LAST_TIME_CONTACTED, 12345); 1131 values.put(Contacts.TIMES_CONTACTED, 54321); 1132 values.put(Contacts.STARRED, 1); 1133 1134 assertStoredValues(Email.CONTENT_URI, values); 1135 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 1136 assertSelection(Email.CONTENT_URI, values, Data._ID, emailId); 1137 1138 // Check if the provider detects duplicated email addresses. 1139 final Uri emailUri2 = insertEmail(rawContactId, "meghan (at) acme.com"); 1140 final long emailId2 = ContentUris.parseId(emailUri2); 1141 final ContentValues values2 = new ContentValues(values); 1142 values2.put(Data._ID, emailId2); 1143 1144 final Uri dedupeUri = Email.CONTENT_URI.buildUpon() 1145 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 1146 .build(); 1147 1148 // URI with ID should return a correct result. 1149 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 1150 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values); 1151 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2); 1152 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2); 1153 1154 assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2}); 1155 1156 // If requested to remove duplicates, the query should return just one result, 1157 // whose _ID won't be deterministic. 1158 values.remove(Data._ID); 1159 assertStoredValues(dedupeUri, values); 1160 } 1161 1162 public void testEmailsLookupQuery() { 1163 long rawContactId = createRawContactWithName("Hot", "Tamale"); 1164 insertEmail(rawContactId, "tamale (at) acme.com"); 1165 1166 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale (at) acme.com"); 1167 ContentValues values = new ContentValues(); 1168 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1169 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1170 values.put(Email.DATA, "tamale (at) acme.com"); 1171 values.put(Email.TYPE, Email.TYPE_HOME); 1172 values.putNull(Email.LABEL); 1173 assertStoredValues(filterUri1, values); 1174 1175 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale (at) acme.com>"); 1176 assertStoredValues(filterUri2, values); 1177 1178 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada (at) acme.com"); 1179 assertEquals(0, getCount(filterUri3, null, null)); 1180 } 1181 1182 public void testEmailsFilterQuery() { 1183 long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1); 1184 insertEmail(rawContactId1, "tamale (at) acme.com"); 1185 insertEmail(rawContactId1, "tamale (at) acme.com"); 1186 1187 long rawContactId2 = createRawContactWithName("Hot", "Tamale", ACCOUNT_2); 1188 insertEmail(rawContactId2, "tamale (at) acme.com"); 1189 1190 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam"); 1191 ContentValues values = new ContentValues(); 1192 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1193 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1194 values.put(Email.DATA, "tamale (at) acme.com"); 1195 values.put(Email.TYPE, Email.TYPE_HOME); 1196 values.putNull(Email.LABEL); 1197 assertStoredValuesWithProjection(filterUri1, values); 1198 1199 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot"); 1200 assertStoredValuesWithProjection(filterUri2, values); 1201 1202 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale"); 1203 assertStoredValuesWithProjection(filterUri3, values); 1204 1205 Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme"); 1206 assertStoredValuesWithProjection(filterUri4, values); 1207 1208 Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada"); 1209 assertEquals(0, getCount(filterUri5, null, null)); 1210 } 1211 1212 /** 1213 * Tests if ContactsProvider2 returns addresses according to registration order. 1214 */ 1215 public void testEmailFilterDefaultSortOrder() { 1216 long rawContactId1 = createRawContact(); 1217 insertEmail(rawContactId1, "address1 (at) email.com"); 1218 insertEmail(rawContactId1, "address2 (at) email.com"); 1219 insertEmail(rawContactId1, "address3 (at) email.com"); 1220 ContentValues v1 = new ContentValues(); 1221 v1.put(Email.ADDRESS, "address1 (at) email.com"); 1222 ContentValues v2 = new ContentValues(); 1223 v2.put(Email.ADDRESS, "address2 (at) email.com"); 1224 ContentValues v3 = new ContentValues(); 1225 v3.put(Email.ADDRESS, "address3 (at) email.com"); 1226 1227 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 1228 assertStoredValuesOrderly(filterUri, new ContentValues[] { v1, v2, v3 }); 1229 } 1230 1231 /** 1232 * Tests if ContactsProvider2 returns primary addresses before the other addresses. 1233 */ 1234 public void testEmailFilterPrimaryAddress() { 1235 long rawContactId1 = createRawContact(); 1236 insertEmail(rawContactId1, "address1 (at) email.com"); 1237 insertEmail(rawContactId1, "address2 (at) email.com", true); 1238 ContentValues v1 = new ContentValues(); 1239 v1.put(Email.ADDRESS, "address1 (at) email.com"); 1240 ContentValues v2 = new ContentValues(); 1241 v2.put(Email.ADDRESS, "address2 (at) email.com"); 1242 1243 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 1244 assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 }); 1245 } 1246 1247 /** 1248 * Tests if ContactsProvider2 has email address associated with a primary account before the 1249 * other address. 1250 */ 1251 public void testEmailFilterPrimaryAccount() { 1252 long rawContactId1 = createRawContact(ACCOUNT_1); 1253 insertEmail(rawContactId1, "account1 (at) email.com"); 1254 long rawContactId2 = createRawContact(ACCOUNT_2); 1255 insertEmail(rawContactId2, "account2 (at) email.com"); 1256 ContentValues v1 = new ContentValues(); 1257 v1.put(Email.ADDRESS, "account1 (at) email.com"); 1258 ContentValues v2 = new ContentValues(); 1259 v2.put(Email.ADDRESS, "account2 (at) email.com"); 1260 1261 Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 1262 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name) 1263 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_1.type) 1264 .build(); 1265 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 }); 1266 1267 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 1268 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name) 1269 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_2.type) 1270 .build(); 1271 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 }); 1272 1273 // Just with PRIMARY_ACCOUNT_NAME 1274 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 1275 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name) 1276 .build(); 1277 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2 }); 1278 1279 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 1280 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name) 1281 .build(); 1282 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 }); 1283 } 1284 1285 /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */ 1286 public void testEmailFilterSortOrderWithFeedback() { 1287 long rawContactId1 = createRawContact(); 1288 String address1 = "address1 (at) email.com"; 1289 insertEmail(rawContactId1, address1); 1290 long rawContactId2 = createRawContact(); 1291 String address2 = "address2 (at) email.com"; 1292 insertEmail(rawContactId2, address2); 1293 String address3 = "address3 (at) email.com"; 1294 ContentUris.parseId(insertEmail(rawContactId2, address3)); 1295 1296 ContentValues v1 = new ContentValues(); 1297 v1.put(Email.ADDRESS, "address1 (at) email.com"); 1298 ContentValues v2 = new ContentValues(); 1299 v2.put(Email.ADDRESS, "address2 (at) email.com"); 1300 ContentValues v3 = new ContentValues(); 1301 v3.put(Email.ADDRESS, "address3 (at) email.com"); 1302 1303 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 1304 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 1305 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 1306 DataUsageFeedback.USAGE_TYPE_CALL) 1307 .build(); 1308 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 1309 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 1310 DataUsageFeedback.USAGE_TYPE_LONG_TEXT) 1311 .build(); 1312 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 1313 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 1314 DataUsageFeedback.USAGE_TYPE_SHORT_TEXT) 1315 .build(); 1316 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 }); 1317 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 }); 1318 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 }); 1319 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 }); 1320 1321 sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3); 1322 1323 // account3 (at) email.com should be the first. account2 (at) email.com should also be promoted as 1324 // it has same contact id. 1325 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v3, v1, v2 }); 1326 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 }); 1327 } 1328 1329 /** 1330 * Tests {@link DataUsageFeedback} correctly bucketize contacts using each 1331 * {@link DataUsageStatColumns#LAST_TIME_USED} 1332 */ 1333 public void testEmailFilterSortOrderWithOldHistory() { 1334 long rawContactId1 = createRawContact(); 1335 long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1 (at) email.com")); 1336 long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2 (at) email.com")); 1337 long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3 (at) email.com")); 1338 long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4 (at) email.com")); 1339 1340 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 1341 1342 ContentValues v1 = new ContentValues(); 1343 v1.put(Email.ADDRESS, "address1 (at) email.com"); 1344 ContentValues v2 = new ContentValues(); 1345 v2.put(Email.ADDRESS, "address2 (at) email.com"); 1346 ContentValues v3 = new ContentValues(); 1347 v3.put(Email.ADDRESS, "address3 (at) email.com"); 1348 ContentValues v4 = new ContentValues(); 1349 v4.put(Email.ADDRESS, "address4 (at) email.com"); 1350 1351 final ContactsProvider2 provider = (ContactsProvider2) getProvider(); 1352 1353 long nowInMillis = System.currentTimeMillis(); 1354 long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000); 1355 long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000); 1356 long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000); 1357 1358 // address4 is contacted just once yesterday. 1359 provider.updateDataUsageStat(Arrays.asList(dataId4), 1360 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis); 1361 1362 // address3 is contacted twice 1 week ago. 1363 provider.updateDataUsageStat(Arrays.asList(dataId3), 1364 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis); 1365 provider.updateDataUsageStat(Arrays.asList(dataId3), 1366 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis); 1367 1368 // address2 is contacted three times 1 year ago. 1369 provider.updateDataUsageStat(Arrays.asList(dataId2), 1370 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 1371 provider.updateDataUsageStat(Arrays.asList(dataId2), 1372 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 1373 provider.updateDataUsageStat(Arrays.asList(dataId2), 1374 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 1375 1376 // auto-complete should prefer recently contacted methods 1377 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 }); 1378 1379 // Pretend address2 is contacted right now 1380 provider.updateDataUsageStat(Arrays.asList(dataId2), 1381 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis); 1382 1383 // Now address2 is the most recently used address 1384 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 }); 1385 1386 // Pretend address1 is contacted right now 1387 provider.updateDataUsageStat(Arrays.asList(dataId1), 1388 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis); 1389 1390 // address2 is preferred to address1 as address2 is used 4 times in total 1391 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 }); 1392 } 1393 1394 public void testPostalsQuery() { 1395 long rawContactId = createRawContactWithName("Alice", "Nextore"); 1396 Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 1397 final long dataId = ContentUris.parseId(dataUri); 1398 1399 final long contactId = queryContactId(rawContactId); 1400 ContentValues values = new ContentValues(); 1401 values.put(Data._ID, dataId); 1402 values.put(Data.RAW_CONTACT_ID, rawContactId); 1403 values.put(RawContacts.CONTACT_ID, contactId); 1404 values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 1405 values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View"); 1406 values.put(Contacts.DISPLAY_NAME, "Alice Nextore"); 1407 1408 assertStoredValues(StructuredPostal.CONTENT_URI, values); 1409 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 1410 values); 1411 assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId); 1412 1413 // Check if the provider detects duplicated addresses. 1414 Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 1415 final long dataId2 = ContentUris.parseId(dataUri2); 1416 final ContentValues values2 = new ContentValues(values); 1417 values2.put(Data._ID, dataId2); 1418 1419 final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon() 1420 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 1421 .build(); 1422 1423 // URI with ID should return a correct result. 1424 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 1425 values); 1426 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values); 1427 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2), 1428 values2); 1429 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2); 1430 1431 assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2}); 1432 1433 // If requested to remove duplicates, the query should return just one result, 1434 // whose _ID won't be deterministic. 1435 values.remove(Data._ID); 1436 assertStoredValues(dedupeUri, values); 1437 } 1438 1439 public void testQueryContactData() { 1440 ContentValues values = new ContentValues(); 1441 long contactId = createContact(values, "John", "Doe", 1442 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 1443 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 1444 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1445 1446 assertStoredValues(contactUri, values); 1447 assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId); 1448 } 1449 1450 public void testQueryContactWithStatusUpdate() { 1451 ContentValues values = new ContentValues(); 1452 long contactId = createContact(values, "John", "Doe", 1453 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 1454 StatusUpdates.CAPABILITY_HAS_CAMERA); 1455 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 1456 values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1457 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1458 assertStoredValuesWithProjection(contactUri, values); 1459 assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId); 1460 } 1461 1462 public void testQueryContactFilterByName() { 1463 ContentValues values = new ContentValues(); 1464 long rawContactId = createRawContact(values, "18004664411", 1465 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 1466 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 1467 StatusUpdates.CAPABILITY_HAS_VOICE); 1468 1469 ContentValues nameValues = new ContentValues(); 1470 nameValues.put(StructuredName.GIVEN_NAME, "Stu"); 1471 nameValues.put(StructuredName.FAMILY_NAME, "Goulash"); 1472 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo"); 1473 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH"); 1474 Uri nameUri = insertStructuredName(rawContactId, nameValues); 1475 1476 long contactId = queryContactId(rawContactId); 1477 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 1478 1479 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash"); 1480 assertStoredValuesWithProjection(filterUri1, values); 1481 1482 assertContactFilter(contactId, "goolash"); 1483 assertContactFilter(contactId, "lash"); 1484 1485 assertContactFilterNoResult("goolish"); 1486 1487 // Phonetic name with given/family reversed should not match 1488 assertContactFilterNoResult("lashgoo"); 1489 1490 nameValues.clear(); 1491 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga"); 1492 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh"); 1493 1494 mResolver.update(nameUri, nameValues, null, null); 1495 1496 assertContactFilter(contactId, "galosh"); 1497 1498 assertContactFilterNoResult("goolish"); 1499 } 1500 1501 public void testQueryContactFilterByEmailAddress() { 1502 ContentValues values = new ContentValues(); 1503 long rawContactId = createRawContact(values, "18004664411", 1504 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 1505 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 1506 StatusUpdates.CAPABILITY_HAS_VOICE); 1507 1508 insertStructuredName(rawContactId, "James", "Bond"); 1509 1510 long contactId = queryContactId(rawContactId); 1511 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 1512 1513 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411 (at) acme.com"); 1514 assertStoredValuesWithProjection(filterUri1, values); 1515 1516 assertContactFilter(contactId, "goog"); 1517 assertContactFilter(contactId, "goog411"); 1518 assertContactFilter(contactId, "goog411@"); 1519 assertContactFilter(contactId, "goog411@acme"); 1520 assertContactFilter(contactId, "goog411 (at) acme.com"); 1521 1522 assertContactFilterNoResult("goog411 (at) acme.combo"); 1523 assertContactFilterNoResult("goog411 (at) le.com"); 1524 assertContactFilterNoResult("goolish"); 1525 } 1526 1527 public void testQueryContactFilterByPhoneNumber() { 1528 ContentValues values = new ContentValues(); 1529 long rawContactId = createRawContact(values, "18004664411", 1530 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 1531 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 1532 StatusUpdates.CAPABILITY_HAS_VOICE); 1533 1534 insertStructuredName(rawContactId, "James", "Bond"); 1535 1536 long contactId = queryContactId(rawContactId); 1537 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 1538 1539 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411"); 1540 assertStoredValuesWithProjection(filterUri1, values); 1541 1542 assertContactFilter(contactId, "18004664411"); 1543 assertContactFilter(contactId, "1800466"); 1544 assertContactFilter(contactId, "+18004664411"); 1545 assertContactFilter(contactId, "8004664411"); 1546 1547 assertContactFilterNoResult("78004664411"); 1548 assertContactFilterNoResult("18004664412"); 1549 assertContactFilterNoResult("8884664411"); 1550 } 1551 1552 /** 1553 * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred 1554 * contacts and frequently used contacts. 1555 */ 1556 public void testQueryContactStrequent() { 1557 ContentValues values1 = new ContentValues(); 1558 final String email1 = "a (at) acme.com"; 1559 final int timesContacted1 = 0; 1560 createContact(values1, "Noah", "Tever", "18004664411", 1561 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0, 1562 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 1563 final String phoneNumber2 = "18004664412"; 1564 ContentValues values2 = new ContentValues(); 1565 createContact(values2, "Sam", "Times", phoneNumber2, 1566 "b (at) acme.com", StatusUpdates.INVISIBLE, 3, 0, 0, 1567 StatusUpdates.CAPABILITY_HAS_CAMERA); 1568 ContentValues values3 = new ContentValues(); 1569 final String phoneNumber3 = "18004664413"; 1570 final int timesContacted3 = 5; 1571 createContact(values3, "Lotta", "Calling", phoneNumber3, 1572 "c (at) acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0, 1573 StatusUpdates.CAPABILITY_HAS_VIDEO); 1574 ContentValues values4 = new ContentValues(); 1575 final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null, 1576 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 1577 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE); 1578 1579 // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data 1580 // usage feedback should be used for "frequently contacted" listing. 1581 assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4); 1582 1583 // Send feedback for the 3rd phone number, pretending we called that person via phone. 1584 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 1585 1586 // After the feedback, 3rd contact should be shown after starred one. 1587 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 1588 new ContentValues[] { values4, values3 }); 1589 1590 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 1591 // Twice. 1592 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 1593 1594 // After the feedback, 1st and 3rd contacts should be shown after starred one. 1595 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 1596 new ContentValues[] { values4, values1, values3 }); 1597 1598 // With phone-only parameter, 1st and 4th contacts shouldn't be returned because: 1599 // 1st: feedbacks are only about email, not about phone call. 1600 // 4th: it has no phone number though starred. 1601 Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon() 1602 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true") 1603 .build(); 1604 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values3 }); 1605 1606 // Now the 4th contact has a phone number. 1607 insertPhoneNumber(rawContactId4, "18004664414"); 1608 1609 // Phone only strequent should return 4th contact. 1610 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 }); 1611 1612 // Send feedback for the 2rd phone number, pretending we send the person a SMS message. 1613 sendFeedback(phoneNumber2, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1); 1614 1615 // SMS feedback shouldn't affect phone-only results. 1616 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 }); 1617 1618 Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay"); 1619 assertStoredValues(filterUri, values4); 1620 } 1621 1622 /** 1623 * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently 1624 * contacted person ordered by number of times contacted. 1625 */ 1626 public void testQueryContactFrequent() { 1627 ContentValues values1 = new ContentValues(); 1628 final String email1 = "a (at) acme.com"; 1629 createContact(values1, "Noah", "Tever", "18004664411", 1630 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 1631 ContentValues values2 = new ContentValues(); 1632 final String email2 = "b (at) acme.com"; 1633 createContact(values2, "Sam", "Times", "18004664412", 1634 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0); 1635 ContentValues values3 = new ContentValues(); 1636 final String phoneNumber3 = "18004664413"; 1637 final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3, 1638 "c (at) acme.com", StatusUpdates.AWAY, 0, 1, 0, 0); 1639 ContentValues values4 = new ContentValues(); 1640 createContact(values4, "Fay", "Veritt", "18004664414", 1641 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0); 1642 1643 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 1644 1645 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, values1); 1646 1647 // Pretend email was sent to the address twice. 1648 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 1649 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 1650 1651 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1}); 1652 1653 // Three times 1654 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 1655 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 1656 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 1657 1658 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 1659 new ContentValues[] {values3, values2, values1}); 1660 1661 // Test it works with selection/selectionArgs 1662 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 1663 Contacts.STARRED + "=?", new String[] {"0"}, 1664 new ContentValues[] {values2, values1}); 1665 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 1666 Contacts.STARRED + "=?", new String[] {"1"}, 1667 new ContentValues[] {values3}); 1668 1669 values3.put(Contacts.STARRED, 0); 1670 assertEquals(1, 1671 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI, 1672 String.valueOf(contactId3)), 1673 values3, null, null)); 1674 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 1675 Contacts.STARRED + "=?", new String[] {"0"}, 1676 new ContentValues[] {values3, values2, values1}); 1677 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 1678 Contacts.STARRED + "=?", new String[] {"1"}, 1679 new ContentValues[] {}); 1680 } 1681 1682 public void testQueryContactGroup() { 1683 long groupId = createGroup(null, "testGroup", "Test Group"); 1684 1685 ContentValues values1 = new ContentValues(); 1686 createContact(values1, "Best", "West", "18004664411", 1687 "west (at) acme.com", StatusUpdates.OFFLINE, 0, 0, groupId, 1688 StatusUpdates.CAPABILITY_HAS_CAMERA); 1689 1690 ContentValues values2 = new ContentValues(); 1691 createContact(values2, "Rest", "East", "18004664422", 1692 "east (at) acme.com", StatusUpdates.AVAILABLE, 0, 0, 0, 1693 StatusUpdates.CAPABILITY_HAS_VOICE); 1694 1695 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 1696 Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID); 1697 assertEquals(1, c.getCount()); 1698 c.moveToFirst(); 1699 assertCursorValues(c, values1); 1700 c.close(); 1701 1702 Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 1703 c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?", 1704 new String[] { "Best West" }, Contacts._ID); 1705 assertEquals(1, c.getCount()); 1706 c.close(); 1707 1708 Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group"); 1709 c = mResolver.query(filterUri3, null, null, null, Contacts._ID); 1710 assertEquals(0, c.getCount()); 1711 c.close(); 1712 } 1713 1714 private void expectSecurityException(String failureMessage, Uri uri, String[] projection, 1715 String selection, String[] selectionArgs, String sortOrder) { 1716 Cursor c = null; 1717 try { 1718 c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder); 1719 fail(failureMessage); 1720 } catch (SecurityException expected) { 1721 // The security exception is expected to occur because we're missing a permission. 1722 } finally { 1723 if (c != null) { 1724 c.close(); 1725 } 1726 } 1727 } 1728 1729 public void testQueryProfileRequiresReadPermission() { 1730 mActor.removePermissions("android.permission.READ_PROFILE"); 1731 1732 createBasicProfileContact(new ContentValues()); 1733 1734 // Case 1: Retrieving profile contact. 1735 expectSecurityException( 1736 "Querying for the profile without READ_PROFILE access should fail.", 1737 Profile.CONTENT_URI, null, null, null, Contacts._ID); 1738 1739 // Case 2: Retrieving profile data. 1740 expectSecurityException( 1741 "Querying for the profile data without READ_PROFILE access should fail.", 1742 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 1743 null, null, null, Contacts._ID); 1744 1745 // Case 3: Retrieving profile entities. 1746 expectSecurityException( 1747 "Querying for the profile entities without READ_PROFILE access should fail.", 1748 Profile.CONTENT_URI.buildUpon() 1749 .appendPath("entities").build(), null, null, null, Contacts._ID); 1750 } 1751 1752 public void testQueryProfileByContactIdRequiresReadPermission() { 1753 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1754 long profileContactId = queryContactId(profileRawContactId); 1755 1756 mActor.removePermissions("android.permission.READ_PROFILE"); 1757 1758 // A query for the profile contact by ID should fail. 1759 expectSecurityException( 1760 "Querying for the profile by contact ID without READ_PROFILE access should fail.", 1761 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId), 1762 null, null, null, Contacts._ID); 1763 } 1764 1765 public void testQueryProfileByRawContactIdRequiresReadPermission() { 1766 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1767 1768 // Remove profile read permission and attempt to retrieve the raw contact. 1769 mActor.removePermissions("android.permission.READ_PROFILE"); 1770 expectSecurityException( 1771 "Querying for the raw contact profile without READ_PROFILE access should fail.", 1772 ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1773 profileRawContactId), null, null, null, RawContacts._ID); 1774 } 1775 1776 public void testQueryProfileRawContactRequiresReadPermission() { 1777 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1778 1779 // Remove profile read permission and attempt to retrieve the profile's raw contact data. 1780 mActor.removePermissions("android.permission.READ_PROFILE"); 1781 1782 // Case 1: Retrieve the overall raw contact set for the profile. 1783 expectSecurityException( 1784 "Querying for the raw contact profile without READ_PROFILE access should fail.", 1785 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null); 1786 1787 // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID. 1788 expectSecurityException( 1789 "Querying for the raw profile data without READ_PROFILE access should fail.", 1790 ContentUris.withAppendedId( 1791 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 1792 .appendPath("data").build(), null, null, null, null); 1793 1794 // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID. 1795 expectSecurityException( 1796 "Querying for the raw profile entities without READ_PROFILE access should fail.", 1797 ContentUris.withAppendedId( 1798 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 1799 .appendPath("entity").build(), null, null, null, null); 1800 } 1801 1802 public void testQueryProfileDataByDataIdRequiresReadPermission() { 1803 createBasicProfileContact(new ContentValues()); 1804 Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 1805 new String[]{Data._ID, Data.MIMETYPE}, null, null, null); 1806 assertEquals(4, c.getCount()); // Photo, phone, email, name. 1807 c.moveToFirst(); 1808 long profileDataId = c.getLong(0); 1809 c.close(); 1810 1811 // Remove profile read permission and attempt to retrieve the data 1812 mActor.removePermissions("android.permission.READ_PROFILE"); 1813 expectSecurityException( 1814 "Querying for the data in the profile without READ_PROFILE access should fail.", 1815 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId), 1816 null, null, null, null); 1817 } 1818 1819 public void testQueryProfileDataRequiresReadPermission() { 1820 createBasicProfileContact(new ContentValues()); 1821 1822 // Remove profile read permission and attempt to retrieve all profile data. 1823 mActor.removePermissions("android.permission.READ_PROFILE"); 1824 expectSecurityException( 1825 "Querying for the data in the profile without READ_PROFILE access should fail.", 1826 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 1827 null, null, null, null); 1828 } 1829 1830 public void testInsertProfileRequiresWritePermission() { 1831 mActor.removePermissions("android.permission.WRITE_PROFILE"); 1832 1833 // Creating a non-profile contact should be fine. 1834 createBasicNonProfileContact(new ContentValues()); 1835 1836 // Creating a profile contact should throw an exception. 1837 try { 1838 createBasicProfileContact(new ContentValues()); 1839 fail("Creating a profile contact should fail without WRITE_PROFILE access."); 1840 } catch (SecurityException expected) { 1841 } 1842 } 1843 1844 public void testInsertProfileDataRequiresWritePermission() { 1845 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1846 1847 mActor.removePermissions("android.permission.WRITE_PROFILE"); 1848 try { 1849 insertEmail(profileRawContactId, "foo (at) bar.net", false); 1850 fail("Inserting data into a profile contact should fail without WRITE_PROFILE access."); 1851 } catch (SecurityException expected) { 1852 } 1853 } 1854 1855 public void testUpdateDataDoesNotRequireProfilePermission() { 1856 mActor.removePermissions("android.permission.READ_PROFILE"); 1857 mActor.removePermissions("android.permission.WRITE_PROFILE"); 1858 1859 // Create a non-profile contact. 1860 long rawContactId = createRawContactWithName("Domo", "Arigato"); 1861 long dataId = getStoredLongValue(Data.CONTENT_URI, 1862 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", 1863 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE}, 1864 Data._ID); 1865 1866 // Updates its name using a selection. 1867 ContentValues values = new ContentValues(); 1868 values.put(StructuredName.GIVEN_NAME, "Bob"); 1869 values.put(StructuredName.FAMILY_NAME, "Blob"); 1870 mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?", 1871 new String[]{String.valueOf(dataId)}); 1872 1873 // Check that the update went through. 1874 assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values); 1875 } 1876 1877 public void testQueryContactThenProfile() { 1878 ContentValues profileValues = new ContentValues(); 1879 long profileRawContactId = createBasicProfileContact(profileValues); 1880 long profileContactId = queryContactId(profileRawContactId); 1881 1882 ContentValues nonProfileValues = new ContentValues(); 1883 long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues); 1884 long nonProfileContactId = queryContactId(nonProfileRawContactId); 1885 1886 assertStoredValues(Contacts.CONTENT_URI, nonProfileValues); 1887 assertSelection(Contacts.CONTENT_URI, nonProfileValues, Contacts._ID, nonProfileContactId); 1888 1889 assertStoredValues(Profile.CONTENT_URI, profileValues); 1890 } 1891 1892 public void testQueryContactExcludeProfile() { 1893 // Create a profile contact (it should not be returned by the general contact URI). 1894 createBasicProfileContact(new ContentValues()); 1895 1896 // Create a non-profile contact - this should be returned. 1897 ContentValues nonProfileValues = new ContentValues(); 1898 createBasicNonProfileContact(nonProfileValues); 1899 1900 assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues}); 1901 } 1902 1903 public void testQueryProfile() { 1904 ContentValues profileValues = new ContentValues(); 1905 createBasicProfileContact(profileValues); 1906 1907 assertStoredValues(Profile.CONTENT_URI, profileValues); 1908 } 1909 1910 private ContentValues[] getExpectedProfileDataValues() { 1911 // Expected photo data values (only field is the photo BLOB, which we can't check). 1912 ContentValues photoRow = new ContentValues(); 1913 photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 1914 1915 // Expected phone data values. 1916 ContentValues phoneRow = new ContentValues(); 1917 phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1918 phoneRow.put(Phone.NUMBER, "18005554411"); 1919 1920 // Expected email data values. 1921 ContentValues emailRow = new ContentValues(); 1922 emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1923 emailRow.put(Email.ADDRESS, "mia.prophyl (at) acme.com"); 1924 1925 // Expected name data values. 1926 ContentValues nameRow = new ContentValues(); 1927 nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 1928 nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl"); 1929 nameRow.put(StructuredName.GIVEN_NAME, "Mia"); 1930 nameRow.put(StructuredName.FAMILY_NAME, "Prophyl"); 1931 1932 return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow}; 1933 } 1934 1935 public void testQueryProfileData() { 1936 createBasicProfileContact(new ContentValues()); 1937 1938 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 1939 getExpectedProfileDataValues()); 1940 } 1941 1942 public void testQueryProfileEntities() { 1943 createBasicProfileContact(new ContentValues()); 1944 1945 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(), 1946 getExpectedProfileDataValues()); 1947 } 1948 1949 public void testQueryRawProfile() { 1950 ContentValues profileValues = new ContentValues(); 1951 createBasicProfileContact(profileValues); 1952 1953 // The raw contact view doesn't include the photo ID. 1954 profileValues.remove(Contacts.PHOTO_ID); 1955 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues); 1956 } 1957 1958 public void testQueryRawProfileById() { 1959 ContentValues profileValues = new ContentValues(); 1960 long profileRawContactId = createBasicProfileContact(profileValues); 1961 1962 // The raw contact view doesn't include the photo ID. 1963 profileValues.remove(Contacts.PHOTO_ID); 1964 assertStoredValues(ContentUris.withAppendedId( 1965 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues); 1966 } 1967 1968 public void testQueryRawProfileData() { 1969 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1970 1971 assertStoredValues(ContentUris.withAppendedId( 1972 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 1973 .appendPath("data").build(), getExpectedProfileDataValues()); 1974 } 1975 1976 public void testQueryRawProfileEntity() { 1977 long profileRawContactId = createBasicProfileContact(new ContentValues()); 1978 1979 assertStoredValues(ContentUris.withAppendedId( 1980 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 1981 .appendPath("entity").build(), getExpectedProfileDataValues()); 1982 } 1983 1984 public void testQueryDataForProfile() { 1985 createBasicProfileContact(new ContentValues()); 1986 1987 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 1988 getExpectedProfileDataValues()); 1989 } 1990 1991 public void testUpdateProfileRawContact() { 1992 createBasicProfileContact(new ContentValues()); 1993 ContentValues updatedValues = new ContentValues(); 1994 updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0); 1995 updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3"); 1996 updatedValues.put(RawContacts.STARRED, 1); 1997 mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null); 1998 1999 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues); 2000 } 2001 2002 public void testInsertProfileWithDataSetTriggersAccountCreation() { 2003 // Check that we have no profile raw contacts. 2004 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{}); 2005 2006 // Insert a profile record with a new data set. 2007 Account account = new Account("a", "b"); 2008 String dataSet = "c"; 2009 Uri profileUri = maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI, account) 2010 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build(); 2011 ContentValues values = new ContentValues(); 2012 long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values)); 2013 values.put(RawContacts._ID, rawContactId); 2014 2015 // Check that querying for the profile gets the created raw contact. 2016 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values); 2017 } 2018 2019 public void testLoadProfilePhoto() throws IOException { 2020 long rawContactId = createBasicProfileContact(new ContentValues()); 2021 insertPhoto(rawContactId, R.drawable.earth_normal); 2022 assertInputStreamContent( 2023 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL), 2024 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false)); 2025 } 2026 2027 public void testLoadProfileDisplayPhoto() throws IOException { 2028 long rawContactId = createBasicProfileContact(new ContentValues()); 2029 insertPhoto(rawContactId, R.drawable.earth_normal); 2030 assertInputStreamContent( 2031 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 2032 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, true)); 2033 } 2034 2035 public void testPhonesWithStatusUpdate() { 2036 2037 ContentValues values = new ContentValues(); 2038 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2039 long rawContactId = ContentUris.parseId(rawContactUri); 2040 insertStructuredName(rawContactId, "John", "Doe"); 2041 Uri photoUri = insertPhoto(rawContactId); 2042 long photoId = ContentUris.parseId(photoUri); 2043 insertPhoneNumber(rawContactId, "18004664411"); 2044 insertPhoneNumber(rawContactId, "18004664412"); 2045 insertEmail(rawContactId, "goog411 (at) acme.com"); 2046 insertEmail(rawContactId, "goog412 (at) acme.com"); 2047 2048 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com", 2049 StatusUpdates.INVISIBLE, "Bad", 2050 StatusUpdates.CAPABILITY_HAS_CAMERA); 2051 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412 (at) acme.com", 2052 StatusUpdates.AVAILABLE, "Good", 2053 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE); 2054 long contactId = queryContactId(rawContactId); 2055 2056 Uri uri = Data.CONTENT_URI; 2057 2058 Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND " 2059 + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER); 2060 assertEquals(2, c.getCount()); 2061 2062 c.moveToFirst(); 2063 2064 values.clear(); 2065 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 2066 values.put(Contacts.CONTACT_STATUS, "Bad"); 2067 values.put(Contacts.DISPLAY_NAME, "John Doe"); 2068 values.put(Phone.NUMBER, "18004664411"); 2069 values.putNull(Phone.LABEL); 2070 values.put(RawContacts.CONTACT_ID, contactId); 2071 assertCursorValues(c, values); 2072 2073 c.moveToNext(); 2074 2075 values.clear(); 2076 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 2077 values.put(Contacts.CONTACT_STATUS, "Bad"); 2078 values.put(Contacts.DISPLAY_NAME, "John Doe"); 2079 values.put(Phone.NUMBER, "18004664412"); 2080 values.putNull(Phone.LABEL); 2081 values.put(RawContacts.CONTACT_ID, contactId); 2082 assertCursorValues(c, values); 2083 2084 c.close(); 2085 } 2086 2087 public void testGroupQuery() { 2088 Account account1 = new Account("a", "b"); 2089 Account account2 = new Account("c", "d"); 2090 long groupId1 = createGroup(account1, "e", "f"); 2091 long groupId2 = createGroup(account2, "g", "h"); 2092 Uri uri1 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1); 2093 Uri uri2 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2); 2094 assertEquals(1, getCount(uri1, null, null)); 2095 assertEquals(1, getCount(uri2, null, null)); 2096 assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ; 2097 assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ; 2098 } 2099 2100 public void testGroupInsert() { 2101 ContentValues values = new ContentValues(); 2102 2103 values.put(Groups.ACCOUNT_NAME, "a"); 2104 values.put(Groups.ACCOUNT_TYPE, "b"); 2105 values.put(Groups.SOURCE_ID, "c"); 2106 values.put(Groups.VERSION, 42); 2107 values.put(Groups.GROUP_VISIBLE, 1); 2108 values.put(Groups.TITLE, "d"); 2109 values.put(Groups.TITLE_RES, 1234); 2110 values.put(Groups.NOTES, "e"); 2111 values.put(Groups.RES_PACKAGE, "f"); 2112 values.put(Groups.SYSTEM_ID, "g"); 2113 values.put(Groups.DELETED, 1); 2114 values.put(Groups.SYNC1, "h"); 2115 values.put(Groups.SYNC2, "i"); 2116 values.put(Groups.SYNC3, "j"); 2117 values.put(Groups.SYNC4, "k"); 2118 2119 Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values); 2120 2121 values.put(Groups.DIRTY, 1); 2122 assertStoredValues(rowUri, values); 2123 } 2124 2125 public void testGroupCreationAfterMembershipInsert() { 2126 long rawContactId1 = createRawContact(mAccount); 2127 Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1"); 2128 2129 long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null); 2130 assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri), 2131 rawContactId1, groupId, "gsid1"); 2132 } 2133 2134 public void testGroupReuseAfterMembershipInsert() { 2135 long rawContactId1 = createRawContact(mAccount); 2136 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 2137 Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1"); 2138 2139 assertSingleGroup(groupId1, mAccount, "gsid1", "title1"); 2140 assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri), 2141 rawContactId1, groupId1, "gsid1"); 2142 } 2143 2144 public void testGroupInsertFailureOnGroupIdConflict() { 2145 long rawContactId1 = createRawContact(mAccount); 2146 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 2147 2148 ContentValues values = new ContentValues(); 2149 values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1); 2150 values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 2151 values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1"); 2152 values.put(GroupMembership.GROUP_ROW_ID, groupId1); 2153 try { 2154 mResolver.insert(Data.CONTENT_URI, values); 2155 fail("the insert was expected to fail, but it succeeded"); 2156 } catch (IllegalArgumentException e) { 2157 // this was expected 2158 } 2159 } 2160 2161 public void testGroupSummaryQuery() { 2162 final Account account1 = new Account("accountName1", "accountType1"); 2163 final Account account2 = new Account("accountName2", "accountType2"); 2164 final long groupId1 = createGroup(account1, "sourceId1", "title1"); 2165 final long groupId2 = createGroup(account2, "sourceId2", "title2"); 2166 final long groupId3 = createGroup(account2, "sourceId3", "title3"); 2167 2168 // Prepare raw contact id not used at all, to test group summary uri won't be confused 2169 // with it. 2170 final long rawContactId0 = createRawContactWithName("firstName0", "lastName0"); 2171 2172 final long rawContactId1 = createRawContactWithName("firstName1", "lastName1"); 2173 insertEmail(rawContactId1, "address1 (at) email.com"); 2174 insertGroupMembership(rawContactId1, groupId1); 2175 2176 final long rawContactId2 = createRawContactWithName("firstName2", "lastName2"); 2177 insertEmail(rawContactId2, "address2 (at) email.com"); 2178 insertPhoneNumber(rawContactId2, "222-222-2222"); 2179 insertGroupMembership(rawContactId2, groupId1); 2180 2181 ContentValues v1 = new ContentValues(); 2182 v1.put(Groups._ID, groupId1); 2183 v1.put(Groups.TITLE, "title1"); 2184 v1.put(Groups.SOURCE_ID, "sourceId1"); 2185 v1.put(Groups.ACCOUNT_NAME, account1.name); 2186 v1.put(Groups.ACCOUNT_TYPE, account1.type); 2187 v1.put(Groups.SUMMARY_COUNT, 2); 2188 v1.put(Groups.SUMMARY_WITH_PHONES, 1); 2189 2190 ContentValues v2 = new ContentValues(); 2191 v2.put(Groups._ID, groupId2); 2192 v2.put(Groups.TITLE, "title2"); 2193 v2.put(Groups.SOURCE_ID, "sourceId2"); 2194 v2.put(Groups.ACCOUNT_NAME, account2.name); 2195 v2.put(Groups.ACCOUNT_TYPE, account2.type); 2196 v2.put(Groups.SUMMARY_COUNT, 0); 2197 v2.put(Groups.SUMMARY_WITH_PHONES, 0); 2198 2199 ContentValues v3 = new ContentValues(); 2200 v3.put(Groups._ID, groupId3); 2201 v3.put(Groups.TITLE, "title3"); 2202 v3.put(Groups.SOURCE_ID, "sourceId3"); 2203 v3.put(Groups.ACCOUNT_NAME, account2.name); 2204 v3.put(Groups.ACCOUNT_TYPE, account2.type); 2205 v3.put(Groups.SUMMARY_COUNT, 0); 2206 v3.put(Groups.SUMMARY_WITH_PHONES, 0); 2207 2208 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 2209 2210 // Now rawContactId1 has two phone numbers. 2211 insertPhoneNumber(rawContactId1, "111-111-1111"); 2212 insertPhoneNumber(rawContactId1, "111-111-1112"); 2213 // Result should reflect it correctly (don't count phone numbers but raw contacts) 2214 v1.put(Groups.SUMMARY_WITH_PHONES, v1.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1); 2215 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 2216 2217 // Introduce new raw contact, pretending the user added another info. 2218 final long rawContactId3 = createRawContactWithName("firstName3", "lastName3"); 2219 insertEmail(rawContactId3, "address3 (at) email.com"); 2220 insertPhoneNumber(rawContactId3, "333-333-3333"); 2221 insertGroupMembership(rawContactId3, groupId2); 2222 v2.put(Groups.SUMMARY_COUNT, v2.getAsInteger(Groups.SUMMARY_COUNT) + 1); 2223 v2.put(Groups.SUMMARY_WITH_PHONES, v2.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1); 2224 2225 assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 }); 2226 2227 final Uri uri = Groups.CONTENT_SUMMARY_URI.buildUpon() 2228 .appendQueryParameter(Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT, "true") 2229 .build(); 2230 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1); 2231 v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2); 2232 v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2); 2233 assertStoredValues(uri, new ContentValues[] { v1, v2, v3 }); 2234 2235 // Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly 2236 // reflects the change. 2237 final long groupId4 = createGroup(account1, "sourceId4", "title4"); 2238 v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2239 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1); 2240 ContentValues v4 = new ContentValues(); 2241 v4.put(Groups._ID, groupId4); 2242 v4.put(Groups.TITLE, "title4"); 2243 v4.put(Groups.SOURCE_ID, "sourceId4"); 2244 v4.put(Groups.ACCOUNT_NAME, account1.name); 2245 v4.put(Groups.ACCOUNT_TYPE, account1.type); 2246 v4.put(Groups.SUMMARY_COUNT, 0); 2247 v4.put(Groups.SUMMARY_WITH_PHONES, 0); 2248 v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2249 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT)); 2250 assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 }); 2251 2252 // We change the tables dynamically according to the requested projection. 2253 // Make sure the SUMMARY_COUNT column exists 2254 v1.clear(); 2255 v1.put(Groups.SUMMARY_COUNT, 2); 2256 v2.clear(); 2257 v2.put(Groups.SUMMARY_COUNT, 1); 2258 v3.clear(); 2259 v3.put(Groups.SUMMARY_COUNT, 0); 2260 v4.clear(); 2261 v4.put(Groups.SUMMARY_COUNT, 0); 2262 assertStoredValuesWithProjection(uri, new ContentValues[] { v1, v2, v3, v4 }); 2263 } 2264 2265 public void testSettingsQuery() { 2266 Account account1 = new Account("a", "b"); 2267 Account account2 = new Account("c", "d"); 2268 AccountWithDataSet account3 = new AccountWithDataSet("e", "f", "plus"); 2269 createSettings(account1, "0", "0"); 2270 createSettings(account2, "1", "1"); 2271 createSettings(account3, "1", "0"); 2272 Uri uri1 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1); 2273 Uri uri2 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2); 2274 Uri uri3 = Settings.CONTENT_URI.buildUpon() 2275 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account3.getAccountName()) 2276 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account3.getAccountType()) 2277 .appendQueryParameter(RawContacts.DATA_SET, account3.getDataSet()) 2278 .build(); 2279 assertEquals(1, getCount(uri1, null, null)); 2280 assertEquals(1, getCount(uri2, null, null)); 2281 assertEquals(1, getCount(uri3, null, null)); 2282 assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ; 2283 assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0"); 2284 assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ; 2285 assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1"); 2286 assertStoredValue(uri3, Settings.SHOULD_SYNC, "1"); 2287 assertStoredValue(uri3, Settings.UNGROUPED_VISIBLE, "0"); 2288 } 2289 2290 public void testSettingsInsertionPreventsDuplicates() { 2291 Account account1 = new Account("a", "b"); 2292 AccountWithDataSet account2 = new AccountWithDataSet("c", "d", "plus"); 2293 createSettings(account1, "0", "0"); 2294 createSettings(account2, "1", "1"); 2295 2296 // Now try creating the settings rows again. It should update the existing settings rows. 2297 createSettings(account1, "1", "0"); 2298 assertStoredValue(Settings.CONTENT_URI, 2299 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?", 2300 new String[] {"a", "b"}, Settings.SHOULD_SYNC, "1"); 2301 2302 createSettings(account2, "0", "1"); 2303 assertStoredValue(Settings.CONTENT_URI, 2304 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=? AND " + 2305 Settings.DATA_SET + "=?", 2306 new String[] {"c", "d", "plus"}, Settings.SHOULD_SYNC, "0"); 2307 } 2308 2309 public void testDisplayNameParsingWhenPartsUnspecified() { 2310 long rawContactId = createRawContact(); 2311 ContentValues values = new ContentValues(); 2312 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 2313 insertStructuredName(rawContactId, values); 2314 2315 assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr."); 2316 } 2317 2318 public void testDisplayNameParsingWhenPartsAreNull() { 2319 long rawContactId = createRawContact(); 2320 ContentValues values = new ContentValues(); 2321 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 2322 values.putNull(StructuredName.GIVEN_NAME); 2323 values.putNull(StructuredName.FAMILY_NAME); 2324 insertStructuredName(rawContactId, values); 2325 assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr."); 2326 } 2327 2328 public void testDisplayNameParsingWhenPartsSpecified() { 2329 long rawContactId = createRawContact(); 2330 ContentValues values = new ContentValues(); 2331 values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr."); 2332 values.put(StructuredName.FAMILY_NAME, "Johnson"); 2333 insertStructuredName(rawContactId, values); 2334 2335 assertStructuredName(rawContactId, null, null, null, "Johnson", null); 2336 } 2337 2338 public void testContactWithoutPhoneticName() { 2339 final long rawContactId = createRawContact(null); 2340 2341 ContentValues values = new ContentValues(); 2342 values.put(StructuredName.PREFIX, "Mr"); 2343 values.put(StructuredName.GIVEN_NAME, "John"); 2344 values.put(StructuredName.MIDDLE_NAME, "K."); 2345 values.put(StructuredName.FAMILY_NAME, "Doe"); 2346 values.put(StructuredName.SUFFIX, "Jr."); 2347 Uri dataUri = insertStructuredName(rawContactId, values); 2348 2349 values.clear(); 2350 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2351 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr."); 2352 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr."); 2353 values.putNull(RawContacts.PHONETIC_NAME); 2354 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2355 values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr."); 2356 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr."); 2357 2358 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 2359 assertStoredValues(rawContactUri, values); 2360 2361 values.clear(); 2362 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2363 values.put(Contacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr."); 2364 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr."); 2365 values.putNull(Contacts.PHONETIC_NAME); 2366 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2367 values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr."); 2368 values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr."); 2369 2370 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 2371 queryContactId(rawContactId)); 2372 assertStoredValues(contactUri, values); 2373 2374 // The same values should be available through a join with Data 2375 assertStoredValues(dataUri, values); 2376 } 2377 2378 public void testContactWithChineseName() { 2379 2380 // Only run this test when Chinese collation is supported 2381 if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) { 2382 return; 2383 } 2384 2385 long rawContactId = createRawContact(null); 2386 2387 ContentValues values = new ContentValues(); 2388 values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B"); 2389 Uri dataUri = insertStructuredName(rawContactId, values); 2390 2391 values.clear(); 2392 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2393 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 2394 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 2395 values.putNull(RawContacts.PHONETIC_NAME); 2396 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2397 values.put(RawContacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B"); 2398 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B"); 2399 2400 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 2401 assertStoredValues(rawContactUri, values); 2402 2403 values.clear(); 2404 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2405 values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B"); 2406 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B"); 2407 values.putNull(Contacts.PHONETIC_NAME); 2408 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2409 values.put(Contacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B"); 2410 values.put(Contacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B"); 2411 2412 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 2413 queryContactId(rawContactId)); 2414 assertStoredValues(contactUri, values); 2415 2416 // The same values should be available through a join with Data 2417 assertStoredValues(dataUri, values); 2418 } 2419 2420 public void testContactWithJapaneseName() { 2421 long rawContactId = createRawContact(null); 2422 2423 ContentValues values = new ContentValues(); 2424 values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77"); 2425 values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046"); 2426 Uri dataUri = insertStructuredName(rawContactId, values); 2427 2428 values.clear(); 2429 values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2430 values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77"); 2431 values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77"); 2432 values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046"); 2433 values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 2434 values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046"); 2435 values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046"); 2436 2437 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 2438 assertStoredValues(rawContactUri, values); 2439 2440 values.clear(); 2441 values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME); 2442 values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77"); 2443 values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77"); 2444 values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046"); 2445 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 2446 values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046"); 2447 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046"); 2448 2449 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 2450 queryContactId(rawContactId)); 2451 assertStoredValues(contactUri, values); 2452 2453 // The same values should be available through a join with Data 2454 assertStoredValues(dataUri, values); 2455 } 2456 2457 public void testDisplayNameUpdate() { 2458 long rawContactId1 = createRawContact(); 2459 insertEmail(rawContactId1, "potato (at) acme.com", true); 2460 2461 long rawContactId2 = createRawContact(); 2462 insertPhoneNumber(rawContactId2, "123456789", true); 2463 2464 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 2465 rawContactId1, rawContactId2); 2466 2467 assertAggregated(rawContactId1, rawContactId2, "123456789"); 2468 2469 insertStructuredName(rawContactId2, "Potato", "Head"); 2470 2471 assertAggregated(rawContactId1, rawContactId2, "Potato Head"); 2472 assertNetworkNotified(true); 2473 } 2474 2475 public void testDisplayNameFromData() { 2476 long rawContactId = createRawContact(); 2477 long contactId = queryContactId(rawContactId); 2478 ContentValues values = new ContentValues(); 2479 2480 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2481 2482 assertStoredValue(uri, Contacts.DISPLAY_NAME, null); 2483 insertEmail(rawContactId, "mike (at) monstersinc.com"); 2484 assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike (at) monstersinc.com"); 2485 2486 insertEmail(rawContactId, "james (at) monstersinc.com", true); 2487 assertStoredValue(uri, Contacts.DISPLAY_NAME, "james (at) monstersinc.com"); 2488 2489 insertPhoneNumber(rawContactId, "1-800-466-4411"); 2490 assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411"); 2491 2492 // If there are title and company, the company is display name. 2493 values.clear(); 2494 values.put(Organization.COMPANY, "Monsters Inc"); 2495 Uri organizationUri = insertOrganization(rawContactId, values); 2496 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc"); 2497 2498 // If there is nickname, that is display name. 2499 insertNickname(rawContactId, "Sully"); 2500 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully"); 2501 2502 // If there is structured name, that is display name. 2503 values.clear(); 2504 values.put(StructuredName.GIVEN_NAME, "James"); 2505 values.put(StructuredName.MIDDLE_NAME, "P."); 2506 values.put(StructuredName.FAMILY_NAME, "Sullivan"); 2507 insertStructuredName(rawContactId, values); 2508 assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan"); 2509 } 2510 2511 public void testDisplayNameFromOrganizationWithoutPhoneticName() { 2512 long rawContactId = createRawContact(); 2513 long contactId = queryContactId(rawContactId); 2514 ContentValues values = new ContentValues(); 2515 2516 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2517 2518 // If there is title without company, the title is display name. 2519 values.clear(); 2520 values.put(Organization.TITLE, "Protagonist"); 2521 Uri organizationUri = insertOrganization(rawContactId, values); 2522 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist"); 2523 2524 // If there are title and company, the company is display name. 2525 values.clear(); 2526 values.put(Organization.COMPANY, "Monsters Inc"); 2527 mResolver.update(organizationUri, values, null, null); 2528 2529 values.clear(); 2530 values.put(Contacts.DISPLAY_NAME, "Monsters Inc"); 2531 values.putNull(Contacts.PHONETIC_NAME); 2532 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2533 values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc"); 2534 values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc"); 2535 assertStoredValues(uri, values); 2536 } 2537 2538 public void testDisplayNameFromOrganizationWithJapanesePhoneticName() { 2539 long rawContactId = createRawContact(); 2540 long contactId = queryContactId(rawContactId); 2541 ContentValues values = new ContentValues(); 2542 2543 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2544 2545 // If there is title without company, the title is display name. 2546 values.clear(); 2547 values.put(Organization.COMPANY, "DoCoMo"); 2548 values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2"); 2549 Uri organizationUri = insertOrganization(rawContactId, values); 2550 2551 values.clear(); 2552 values.put(Contacts.DISPLAY_NAME, "DoCoMo"); 2553 values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2"); 2554 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE); 2555 values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2"); 2556 values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2"); 2557 assertStoredValues(uri, values); 2558 } 2559 2560 public void testDisplayNameFromOrganizationWithChineseName() { 2561 boolean hasChineseCollator = false; 2562 final Locale locale[] = Collator.getAvailableLocales(); 2563 for (int i = 0; i < locale.length; i++) { 2564 if (locale[i].equals(Locale.CHINA)) { 2565 hasChineseCollator = true; 2566 break; 2567 } 2568 } 2569 2570 if (!hasChineseCollator) { 2571 return; 2572 } 2573 2574 long rawContactId = createRawContact(); 2575 long contactId = queryContactId(rawContactId); 2576 ContentValues values = new ContentValues(); 2577 2578 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2579 2580 // If there is title without company, the title is display name. 2581 values.clear(); 2582 values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1"); 2583 Uri organizationUri = insertOrganization(rawContactId, values); 2584 2585 values.clear(); 2586 values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1"); 2587 values.putNull(Contacts.PHONETIC_NAME); 2588 values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED); 2589 values.put(Contacts.SORT_KEY_PRIMARY, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1"); 2590 values.put(Contacts.SORT_KEY_ALTERNATIVE, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1"); 2591 assertStoredValues(uri, values); 2592 } 2593 2594 public void testLookupByOrganization() { 2595 long rawContactId = createRawContact(); 2596 long contactId = queryContactId(rawContactId); 2597 ContentValues values = new ContentValues(); 2598 2599 values.clear(); 2600 values.put(Organization.COMPANY, "acmecorp"); 2601 values.put(Organization.TITLE, "president"); 2602 Uri organizationUri = insertOrganization(rawContactId, values); 2603 2604 assertContactFilter(contactId, "acmecorp"); 2605 assertContactFilter(contactId, "president"); 2606 2607 values.clear(); 2608 values.put(Organization.DEPARTMENT, "software"); 2609 mResolver.update(organizationUri, values, null, null); 2610 2611 assertContactFilter(contactId, "acmecorp"); 2612 assertContactFilter(contactId, "president"); 2613 2614 values.clear(); 2615 values.put(Organization.COMPANY, "incredibles"); 2616 mResolver.update(organizationUri, values, null, null); 2617 2618 assertContactFilter(contactId, "incredibles"); 2619 assertContactFilter(contactId, "president"); 2620 2621 values.clear(); 2622 values.put(Organization.TITLE, "director"); 2623 mResolver.update(organizationUri, values, null, null); 2624 2625 assertContactFilter(contactId, "incredibles"); 2626 assertContactFilter(contactId, "director"); 2627 2628 values.clear(); 2629 values.put(Organization.COMPANY, "monsters"); 2630 values.put(Organization.TITLE, "scarer"); 2631 mResolver.update(organizationUri, values, null, null); 2632 2633 assertContactFilter(contactId, "monsters"); 2634 assertContactFilter(contactId, "scarer"); 2635 } 2636 2637 private void assertContactFilter(long contactId, String filter) { 2638 Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter)); 2639 assertStoredValue(filterUri, Contacts._ID, contactId); 2640 } 2641 2642 private void assertContactFilterNoResult(String filter) { 2643 Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, filter); 2644 assertEquals(0, getCount(filterUri4, null, null)); 2645 } 2646 2647 public void testSearchSnippetOrganization() throws Exception { 2648 long rawContactId = createRawContactWithName(); 2649 long contactId = queryContactId(rawContactId); 2650 2651 // Some random data element 2652 insertEmail(rawContactId, "inc (at) corp.com"); 2653 2654 ContentValues values = new ContentValues(); 2655 values.clear(); 2656 values.put(Organization.COMPANY, "acmecorp"); 2657 values.put(Organization.TITLE, "engineer"); 2658 Uri organizationUri = insertOrganization(rawContactId, values); 2659 2660 // Add another matching organization 2661 values.put(Organization.COMPANY, "acmeinc"); 2662 insertOrganization(rawContactId, values); 2663 2664 // Add another non-matching organization 2665 values.put(Organization.COMPANY, "corpacme"); 2666 insertOrganization(rawContactId, values); 2667 2668 // And another data element 2669 insertEmail(rawContactId, "emca (at) corp.com", true, Email.TYPE_CUSTOM, "Custom"); 2670 2671 Uri filterUri = buildFilterUri("acme", true); 2672 2673 values.clear(); 2674 values.put(Contacts._ID, contactId); 2675 values.put(SearchSnippetColumns.SNIPPET, "engineer, [acmecorp]"); 2676 assertStoredValues(filterUri, values); 2677 } 2678 2679 public void testSearchSnippetEmail() throws Exception { 2680 long rawContactId = createRawContact(); 2681 long contactId = queryContactId(rawContactId); 2682 ContentValues values = new ContentValues(); 2683 2684 insertStructuredName(rawContactId, "John", "Doe"); 2685 Uri dataUri = insertEmail(rawContactId, "acme (at) corp.com", true, Email.TYPE_CUSTOM, "Custom"); 2686 2687 Uri filterUri = buildFilterUri("acme", true); 2688 2689 values.clear(); 2690 values.put(Contacts._ID, contactId); 2691 values.put(SearchSnippetColumns.SNIPPET, "[acme (at) corp.com]"); 2692 assertStoredValues(filterUri, values); 2693 } 2694 2695 public void testSearchSnippetPhone() throws Exception { 2696 long rawContactId = createRawContact(); 2697 long contactId = queryContactId(rawContactId); 2698 ContentValues values = new ContentValues(); 2699 2700 insertStructuredName(rawContactId, "Cave", "Johnson"); 2701 insertPhoneNumber(rawContactId, "(860) 555-1234"); 2702 2703 values.clear(); 2704 values.put(Contacts._ID, contactId); 2705 values.put(SearchSnippetColumns.SNIPPET, "[(860) 555-1234]"); 2706 2707 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2708 Uri.encode("86 (0) 5-55-12-34")), values); 2709 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2710 Uri.encode("860 555-1234")), values); 2711 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2712 Uri.encode("860")), values); 2713 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2714 Uri.encode("8605551234")), values); 2715 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2716 Uri.encode("860555")), values); 2717 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2718 Uri.encode("860 555")), values); 2719 assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 2720 Uri.encode("860-555")), values); 2721 } 2722 2723 private Uri buildFilterUri(String query, boolean deferredSnippeting) { 2724 Uri.Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon() 2725 .appendPath(Uri.encode(query)); 2726 if (deferredSnippeting) { 2727 builder.appendQueryParameter(ContactsContract.DEFERRED_SNIPPETING, "1"); 2728 } 2729 return builder.build(); 2730 } 2731 2732 public void testSearchSnippetNickname() throws Exception { 2733 long rawContactId = createRawContactWithName(); 2734 long contactId = queryContactId(rawContactId); 2735 ContentValues values = new ContentValues(); 2736 2737 Uri dataUri = insertNickname(rawContactId, "Incredible"); 2738 2739 Uri filterUri = buildFilterUri("inc", true); 2740 2741 values.clear(); 2742 values.put(Contacts._ID, contactId); 2743 values.put(SearchSnippetColumns.SNIPPET, "[Incredible]"); 2744 assertStoredValues(filterUri, values); 2745 } 2746 2747 public void testSearchSnippetEmptyForNameInDisplayName() throws Exception { 2748 long rawContactId = createRawContact(); 2749 long contactId = queryContactId(rawContactId); 2750 insertStructuredName(rawContactId, "Cave", "Johnson"); 2751 insertEmail(rawContactId, "cave (at) aperturescience.com", true); 2752 2753 ContentValues emptySnippet = new ContentValues(); 2754 emptySnippet.clear(); 2755 emptySnippet.put(Contacts._ID, contactId); 2756 emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null); 2757 2758 assertStoredValues(buildFilterUri("cave", true), emptySnippet); 2759 assertStoredValues(buildFilterUri("john", true), emptySnippet); 2760 } 2761 2762 public void testSearchSnippetEmptyForNicknameInDisplayName() throws Exception { 2763 long rawContactId = createRawContact(); 2764 long contactId = queryContactId(rawContactId); 2765 insertNickname(rawContactId, "Caveman"); 2766 insertEmail(rawContactId, "cave (at) aperturescience.com", true); 2767 2768 ContentValues emptySnippet = new ContentValues(); 2769 emptySnippet.clear(); 2770 emptySnippet.put(Contacts._ID, contactId); 2771 emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null); 2772 2773 assertStoredValues(buildFilterUri("cave", true), emptySnippet); 2774 } 2775 2776 public void testSearchSnippetEmptyForCompanyInDisplayName() throws Exception { 2777 long rawContactId = createRawContact(); 2778 long contactId = queryContactId(rawContactId); 2779 ContentValues company = new ContentValues(); 2780 company.clear(); 2781 company.put(Organization.COMPANY, "Aperture Science"); 2782 company.put(Organization.TITLE, "President"); 2783 insertOrganization(rawContactId, company); 2784 insertEmail(rawContactId, "aperturepresident (at) aperturescience.com", true); 2785 2786 ContentValues emptySnippet = new ContentValues(); 2787 emptySnippet.clear(); 2788 emptySnippet.put(Contacts._ID, contactId); 2789 emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null); 2790 2791 assertStoredValues(buildFilterUri("aperture", true), emptySnippet); 2792 } 2793 2794 public void testSearchSnippetEmptyForPhoneInDisplayName() throws Exception { 2795 long rawContactId = createRawContact(); 2796 long contactId = queryContactId(rawContactId); 2797 insertPhoneNumber(rawContactId, "860-555-1234"); 2798 insertEmail(rawContactId, "860 (at) aperturescience.com", true); 2799 2800 ContentValues emptySnippet = new ContentValues(); 2801 emptySnippet.clear(); 2802 emptySnippet.put(Contacts._ID, contactId); 2803 emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null); 2804 2805 assertStoredValues(buildFilterUri("860", true), emptySnippet); 2806 } 2807 2808 public void testSearchSnippetEmptyForEmailInDisplayName() throws Exception { 2809 long rawContactId = createRawContact(); 2810 long contactId = queryContactId(rawContactId); 2811 insertEmail(rawContactId, "cave (at) aperturescience.com", true); 2812 insertNote(rawContactId, "Cave Johnson is president of Aperture Science"); 2813 2814 ContentValues emptySnippet = new ContentValues(); 2815 emptySnippet.clear(); 2816 emptySnippet.put(Contacts._ID, contactId); 2817 emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null); 2818 2819 assertStoredValues(buildFilterUri("cave", true), emptySnippet); 2820 } 2821 2822 public void testDisplayNameUpdateFromStructuredNameUpdate() { 2823 long rawContactId = createRawContact(); 2824 Uri nameUri = insertStructuredName(rawContactId, "Slinky", "Dog"); 2825 2826 long contactId = queryContactId(rawContactId); 2827 2828 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2829 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog"); 2830 2831 ContentValues values = new ContentValues(); 2832 values.putNull(StructuredName.FAMILY_NAME); 2833 2834 mResolver.update(nameUri, values, null, null); 2835 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky"); 2836 2837 values.putNull(StructuredName.GIVEN_NAME); 2838 2839 mResolver.update(nameUri, values, null, null); 2840 assertStoredValue(uri, Contacts.DISPLAY_NAME, null); 2841 2842 values.put(StructuredName.FAMILY_NAME, "Dog"); 2843 mResolver.update(nameUri, values, null, null); 2844 2845 assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog"); 2846 } 2847 2848 public void testInsertDataWithContentProviderOperations() throws Exception { 2849 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 2850 .withValues(new ContentValues()) 2851 .build(); 2852 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 2853 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 2854 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 2855 .withValue(StructuredName.GIVEN_NAME, "John") 2856 .withValue(StructuredName.FAMILY_NAME, "Doe") 2857 .build(); 2858 ContentProviderResult[] results = 2859 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2)); 2860 long contactId = queryContactId(ContentUris.parseId(results[0].uri)); 2861 Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2862 assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe"); 2863 } 2864 2865 public void testSendToVoicemailDefault() { 2866 long rawContactId = createRawContactWithName(); 2867 long contactId = queryContactId(rawContactId); 2868 2869 Cursor c = queryContact(contactId); 2870 assertTrue(c.moveToNext()); 2871 int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL)); 2872 assertEquals(0, sendToVoicemail); 2873 c.close(); 2874 } 2875 2876 public void testSetSendToVoicemailAndRingtone() { 2877 long rawContactId = createRawContactWithName(); 2878 long contactId = queryContactId(rawContactId); 2879 2880 updateSendToVoicemailAndRingtone(contactId, true, "foo"); 2881 assertSendToVoicemailAndRingtone(contactId, true, "foo"); 2882 assertNetworkNotified(false); 2883 2884 updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar"); 2885 assertSendToVoicemailAndRingtone(contactId, false, "bar"); 2886 assertNetworkNotified(false); 2887 } 2888 2889 public void testSendToVoicemailAndRingtoneAfterAggregation() { 2890 long rawContactId1 = createRawContactWithName("a", "b"); 2891 long contactId1 = queryContactId(rawContactId1); 2892 updateSendToVoicemailAndRingtone(contactId1, true, "foo"); 2893 2894 long rawContactId2 = createRawContactWithName("c", "d"); 2895 long contactId2 = queryContactId(rawContactId2); 2896 updateSendToVoicemailAndRingtone(contactId2, true, "bar"); 2897 2898 // Aggregate them 2899 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 2900 rawContactId1, rawContactId2); 2901 2902 // Both contacts had "send to VM", the contact now has the same value 2903 assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar 2904 } 2905 2906 public void testDoNotSendToVoicemailAfterAggregation() { 2907 long rawContactId1 = createRawContactWithName("e", "f"); 2908 long contactId1 = queryContactId(rawContactId1); 2909 updateSendToVoicemailAndRingtone(contactId1, true, null); 2910 2911 long rawContactId2 = createRawContactWithName("g", "h"); 2912 long contactId2 = queryContactId(rawContactId2); 2913 updateSendToVoicemailAndRingtone(contactId2, false, null); 2914 2915 // Aggregate them 2916 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 2917 rawContactId1, rawContactId2); 2918 2919 // Since one of the contacts had "don't send to VM" that setting wins for the aggregate 2920 assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null); 2921 } 2922 2923 public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() { 2924 long rawContactId1 = createRawContactWithName("i", "j"); 2925 long contactId1 = queryContactId(rawContactId1); 2926 updateSendToVoicemailAndRingtone(contactId1, true, "foo"); 2927 2928 long rawContactId2 = createRawContactWithName("k", "l"); 2929 long contactId2 = queryContactId(rawContactId2); 2930 updateSendToVoicemailAndRingtone(contactId2, false, "bar"); 2931 2932 // Aggregate them 2933 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 2934 rawContactId1, rawContactId2); 2935 2936 // Split them 2937 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 2938 rawContactId1, rawContactId2); 2939 2940 assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo"); 2941 assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar"); 2942 } 2943 2944 public void testStatusUpdateInsert() { 2945 long rawContactId = createRawContact(); 2946 Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 2947 long dataId = ContentUris.parseId(imUri); 2948 2949 ContentValues values = new ContentValues(); 2950 values.put(StatusUpdates.DATA_ID, dataId); 2951 values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM); 2952 values.putNull(StatusUpdates.CUSTOM_PROTOCOL); 2953 values.put(StatusUpdates.IM_HANDLE, "aim"); 2954 values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE); 2955 values.put(StatusUpdates.STATUS, "Hiding"); 2956 values.put(StatusUpdates.STATUS_TIMESTAMP, 100); 2957 values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c"); 2958 values.put(StatusUpdates.STATUS_ICON, 1234); 2959 values.put(StatusUpdates.STATUS_LABEL, 2345); 2960 2961 Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values); 2962 2963 assertStoredValues(resultUri, values); 2964 2965 long contactId = queryContactId(rawContactId); 2966 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2967 2968 values.clear(); 2969 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2970 values.put(Contacts.CONTACT_STATUS, "Hiding"); 2971 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100); 2972 values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c"); 2973 values.put(Contacts.CONTACT_STATUS_ICON, 1234); 2974 values.put(Contacts.CONTACT_STATUS_LABEL, 2345); 2975 2976 assertStoredValues(contactUri, values); 2977 2978 values.clear(); 2979 values.put(StatusUpdates.DATA_ID, dataId); 2980 values.put(StatusUpdates.STATUS, "Cloaked"); 2981 values.put(StatusUpdates.STATUS_TIMESTAMP, 200); 2982 values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f"); 2983 values.put(StatusUpdates.STATUS_ICON, 4321); 2984 values.put(StatusUpdates.STATUS_LABEL, 5432); 2985 mResolver.insert(StatusUpdates.CONTENT_URI, values); 2986 2987 values.clear(); 2988 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2989 values.put(Contacts.CONTACT_STATUS, "Cloaked"); 2990 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200); 2991 values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f"); 2992 values.put(Contacts.CONTACT_STATUS_ICON, 4321); 2993 values.put(Contacts.CONTACT_STATUS_LABEL, 5432); 2994 assertStoredValues(contactUri, values); 2995 } 2996 2997 public void testStatusUpdateInferAttribution() { 2998 long rawContactId = createRawContact(); 2999 Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 3000 long dataId = ContentUris.parseId(imUri); 3001 3002 ContentValues values = new ContentValues(); 3003 values.put(StatusUpdates.DATA_ID, dataId); 3004 values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM); 3005 values.put(StatusUpdates.IM_HANDLE, "aim"); 3006 values.put(StatusUpdates.STATUS, "Hiding"); 3007 3008 Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values); 3009 3010 values.clear(); 3011 values.put(StatusUpdates.DATA_ID, dataId); 3012 values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim); 3013 values.put(StatusUpdates.STATUS, "Hiding"); 3014 3015 assertStoredValues(resultUri, values); 3016 } 3017 3018 public void testStatusUpdateMatchingImOrEmail() { 3019 long rawContactId = createRawContact(); 3020 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 3021 insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im"); 3022 insertEmail(rawContactId, "m (at) acme.com"); 3023 3024 // Match on IM (standard) 3025 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 3026 StatusUpdates.CAPABILITY_HAS_CAMERA); 3027 3028 // Match on IM (custom) 3029 insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle", 3030 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 3031 3032 // Match on Email 3033 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m (at) acme.com", StatusUpdates.AWAY, "Away", 3034 StatusUpdates.CAPABILITY_HAS_VOICE); 3035 3036 // No match 3037 insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away", 3038 StatusUpdates.CAPABILITY_HAS_CAMERA); 3039 3040 Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] { 3041 StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL, 3042 StatusUpdates.PRESENCE, StatusUpdates.STATUS}, 3043 PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID); 3044 assertTrue(c.moveToNext()); 3045 assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available"); 3046 assertTrue(c.moveToNext()); 3047 assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle"); 3048 assertTrue(c.moveToNext()); 3049 assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away"); 3050 assertFalse(c.moveToNext()); 3051 c.close(); 3052 3053 long contactId = queryContactId(rawContactId); 3054 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3055 3056 ContentValues values = new ContentValues(); 3057 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 3058 values.put(Contacts.CONTACT_STATUS, "Available"); 3059 assertStoredValuesWithProjection(contactUri, values); 3060 } 3061 3062 public void testStatusUpdateUpdateAndDelete() { 3063 long rawContactId = createRawContact(); 3064 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 3065 3066 long contactId = queryContactId(rawContactId); 3067 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3068 3069 ContentValues values = new ContentValues(); 3070 values.putNull(Contacts.CONTACT_PRESENCE); 3071 values.putNull(Contacts.CONTACT_STATUS); 3072 assertStoredValuesWithProjection(contactUri, values); 3073 3074 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY", 3075 StatusUpdates.CAPABILITY_HAS_CAMERA); 3076 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY", 3077 StatusUpdates.CAPABILITY_HAS_CAMERA); 3078 Uri statusUri = 3079 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 3080 StatusUpdates.CAPABILITY_HAS_CAMERA); 3081 long statusId = ContentUris.parseId(statusUri); 3082 3083 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 3084 values.put(Contacts.CONTACT_STATUS, "Available"); 3085 assertStoredValuesWithProjection(contactUri, values); 3086 3087 // update status_updates table to set new values for 3088 // status_updates.status 3089 // status_updates.status_ts 3090 // presence 3091 long updatedTs = 200; 3092 String testUpdate = "test_update"; 3093 String selection = StatusUpdates.DATA_ID + "=" + statusId; 3094 values.clear(); 3095 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 3096 values.put(StatusUpdates.STATUS, testUpdate); 3097 values.put(StatusUpdates.PRESENCE, "presence_test"); 3098 mResolver.update(StatusUpdates.CONTENT_URI, values, 3099 StatusUpdates.DATA_ID + "=" + statusId, null); 3100 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 3101 3102 // update status_updates table to set new values for columns in status_updates table ONLY 3103 // i.e., no rows in presence table are to be updated. 3104 updatedTs = 300; 3105 testUpdate = "test_update_new"; 3106 selection = StatusUpdates.DATA_ID + "=" + statusId; 3107 values.clear(); 3108 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 3109 values.put(StatusUpdates.STATUS, testUpdate); 3110 mResolver.update(StatusUpdates.CONTENT_URI, values, 3111 StatusUpdates.DATA_ID + "=" + statusId, null); 3112 // make sure the presence column value is still the old value 3113 values.put(StatusUpdates.PRESENCE, "presence_test"); 3114 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 3115 3116 // update status_updates table to set new values for columns in presence table ONLY 3117 // i.e., no rows in status_updates table are to be updated. 3118 selection = StatusUpdates.DATA_ID + "=" + statusId; 3119 values.clear(); 3120 values.put(StatusUpdates.PRESENCE, "presence_test_new"); 3121 mResolver.update(StatusUpdates.CONTENT_URI, values, 3122 StatusUpdates.DATA_ID + "=" + statusId, null); 3123 // make sure the status_updates table is not updated 3124 values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs); 3125 values.put(StatusUpdates.STATUS, testUpdate); 3126 assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values); 3127 3128 // effect "delete status_updates" operation and expect the following 3129 // data deleted from status_updates table 3130 // presence set to null 3131 mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null); 3132 values.clear(); 3133 values.putNull(Contacts.CONTACT_PRESENCE); 3134 assertStoredValuesWithProjection(contactUri, values); 3135 } 3136 3137 public void testStatusUpdateUpdateToNull() { 3138 long rawContactId = createRawContact(); 3139 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 3140 3141 long contactId = queryContactId(rawContactId); 3142 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3143 3144 ContentValues values = new ContentValues(); 3145 Uri statusUri = 3146 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available", 3147 StatusUpdates.CAPABILITY_HAS_CAMERA); 3148 long statusId = ContentUris.parseId(statusUri); 3149 3150 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE); 3151 values.put(Contacts.CONTACT_STATUS, "Available"); 3152 assertStoredValuesWithProjection(contactUri, values); 3153 3154 values.clear(); 3155 values.putNull(StatusUpdates.PRESENCE); 3156 mResolver.update(StatusUpdates.CONTENT_URI, values, 3157 StatusUpdates.DATA_ID + "=" + statusId, null); 3158 3159 values.clear(); 3160 values.putNull(Contacts.CONTACT_PRESENCE); 3161 values.put(Contacts.CONTACT_STATUS, "Available"); 3162 assertStoredValuesWithProjection(contactUri, values); 3163 } 3164 3165 public void testStatusUpdateWithTimestamp() { 3166 long rawContactId = createRawContact(); 3167 insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim"); 3168 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 3169 3170 long contactId = queryContactId(rawContactId); 3171 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 3172 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80, 3173 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 3174 insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100, 3175 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 3176 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90, 3177 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 3178 3179 // Should return the latest status 3180 ContentValues values = new ContentValues(); 3181 values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100); 3182 values.put(Contacts.CONTACT_STATUS, "Available"); 3183 assertStoredValuesWithProjection(contactUri, values); 3184 } 3185 3186 private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence, 3187 String status) { 3188 ContentValues values = new ContentValues(); 3189 values.put(StatusUpdates.PROTOCOL, protocol); 3190 values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol); 3191 values.put(StatusUpdates.PRESENCE, presence); 3192 values.put(StatusUpdates.STATUS, status); 3193 assertCursorValues(c, values); 3194 } 3195 3196 // Stream item query test cases. 3197 3198 public void testQueryStreamItemsByRawContactId() { 3199 long rawContactId = createRawContact(mAccount); 3200 ContentValues values = buildGenericStreamItemValues(); 3201 insertStreamItem(rawContactId, values, mAccount); 3202 assertStoredValues( 3203 Uri.withAppendedPath( 3204 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3205 RawContacts.StreamItems.CONTENT_DIRECTORY), 3206 values); 3207 } 3208 3209 public void testQueryStreamItemsByContactId() { 3210 long rawContactId = createRawContact(); 3211 long contactId = queryContactId(rawContactId); 3212 ContentValues values = buildGenericStreamItemValues(); 3213 insertStreamItem(rawContactId, values, null); 3214 assertStoredValues( 3215 Uri.withAppendedPath( 3216 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 3217 Contacts.StreamItems.CONTENT_DIRECTORY), 3218 values); 3219 } 3220 3221 public void testQueryStreamItemsByLookupKey() { 3222 long rawContactId = createRawContact(); 3223 long contactId = queryContactId(rawContactId); 3224 String lookupKey = queryLookupKey(contactId); 3225 ContentValues values = buildGenericStreamItemValues(); 3226 insertStreamItem(rawContactId, values, null); 3227 assertStoredValues( 3228 Uri.withAppendedPath( 3229 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), 3230 Contacts.StreamItems.CONTENT_DIRECTORY), 3231 values); 3232 } 3233 3234 public void testQueryStreamItemsByLookupKeyAndContactId() { 3235 long rawContactId = createRawContact(); 3236 long contactId = queryContactId(rawContactId); 3237 String lookupKey = queryLookupKey(contactId); 3238 ContentValues values = buildGenericStreamItemValues(); 3239 insertStreamItem(rawContactId, values, null); 3240 assertStoredValues( 3241 Uri.withAppendedPath( 3242 ContentUris.withAppendedId( 3243 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), 3244 contactId), 3245 Contacts.StreamItems.CONTENT_DIRECTORY), 3246 values); 3247 } 3248 3249 public void testQueryStreamItems() { 3250 long rawContactId = createRawContact(); 3251 ContentValues values = buildGenericStreamItemValues(); 3252 insertStreamItem(rawContactId, values, null); 3253 assertStoredValues(StreamItems.CONTENT_URI, values); 3254 } 3255 3256 public void testQueryStreamItemsWithSelection() { 3257 long rawContactId = createRawContact(); 3258 ContentValues firstValues = buildGenericStreamItemValues(); 3259 insertStreamItem(rawContactId, firstValues, null); 3260 3261 ContentValues secondValues = buildGenericStreamItemValues(); 3262 secondValues.put(StreamItems.TEXT, "Goodbye world"); 3263 insertStreamItem(rawContactId, secondValues, null); 3264 3265 // Select only the first stream item. 3266 assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 3267 new String[]{"Hello world"}, firstValues); 3268 3269 // Select only the second stream item. 3270 assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 3271 new String[]{"Goodbye world"}, secondValues); 3272 } 3273 3274 public void testQueryStreamItemById() { 3275 long rawContactId = createRawContact(); 3276 ContentValues firstValues = buildGenericStreamItemValues(); 3277 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 3278 long firstStreamItemId = ContentUris.parseId(resultUri); 3279 3280 ContentValues secondValues = buildGenericStreamItemValues(); 3281 secondValues.put(StreamItems.TEXT, "Goodbye world"); 3282 resultUri = insertStreamItem(rawContactId, secondValues, null); 3283 long secondStreamItemId = ContentUris.parseId(resultUri); 3284 3285 // Select only the first stream item. 3286 assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 3287 firstValues); 3288 3289 // Select only the second stream item. 3290 assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 3291 secondValues); 3292 } 3293 3294 // Stream item photo insertion + query test cases. 3295 3296 public void testQueryStreamItemPhotoWithSelection() { 3297 long rawContactId = createRawContact(); 3298 ContentValues values = buildGenericStreamItemValues(); 3299 Uri resultUri = insertStreamItem(rawContactId, values, null); 3300 long streamItemId = ContentUris.parseId(resultUri); 3301 3302 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 3303 insertStreamItemPhoto(streamItemId, photo1Values, null); 3304 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3305 ContentValues photo2Values = buildGenericStreamItemPhotoValues(2); 3306 insertStreamItemPhoto(streamItemId, photo2Values, null); 3307 3308 // Select only the first photo. 3309 assertStoredValues(StreamItems.CONTENT_PHOTO_URI, StreamItemPhotos.SORT_INDEX + "=?", 3310 new String[]{"1"}, photo1Values); 3311 } 3312 3313 public void testQueryStreamItemPhotoByStreamItemId() { 3314 long rawContactId = createRawContact(); 3315 3316 // Insert a first stream item. 3317 ContentValues firstValues = buildGenericStreamItemValues(); 3318 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 3319 long firstStreamItemId = ContentUris.parseId(resultUri); 3320 3321 // Insert a second stream item. 3322 ContentValues secondValues = buildGenericStreamItemValues(); 3323 resultUri = insertStreamItem(rawContactId, secondValues, null); 3324 long secondStreamItemId = ContentUris.parseId(resultUri); 3325 3326 // Add a photo to the first stream item. 3327 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 3328 insertStreamItemPhoto(firstStreamItemId, photo1Values, null); 3329 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3330 3331 // Add a photo to the second stream item. 3332 ContentValues photo2Values = buildGenericStreamItemPhotoValues(1); 3333 photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 3334 R.drawable.nebula, PhotoSize.ORIGINAL)); 3335 insertStreamItemPhoto(secondStreamItemId, photo2Values, null); 3336 photo2Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3337 3338 // Select only the photos from the second stream item. 3339 assertStoredValues(Uri.withAppendedPath( 3340 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 3341 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), photo2Values); 3342 } 3343 3344 public void testQueryStreamItemPhotoByStreamItemPhotoId() { 3345 long rawContactId = createRawContact(); 3346 3347 // Insert a first stream item. 3348 ContentValues firstValues = buildGenericStreamItemValues(); 3349 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 3350 long firstStreamItemId = ContentUris.parseId(resultUri); 3351 3352 // Insert a second stream item. 3353 ContentValues secondValues = buildGenericStreamItemValues(); 3354 resultUri = insertStreamItem(rawContactId, secondValues, null); 3355 long secondStreamItemId = ContentUris.parseId(resultUri); 3356 3357 // Add a photo to the first stream item. 3358 ContentValues photo1Values = buildGenericStreamItemPhotoValues(1); 3359 resultUri = insertStreamItemPhoto(firstStreamItemId, photo1Values, null); 3360 long firstPhotoId = ContentUris.parseId(resultUri); 3361 photo1Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3362 3363 // Add a photo to the second stream item. 3364 ContentValues photo2Values = buildGenericStreamItemPhotoValues(1); 3365 photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 3366 R.drawable.galaxy, PhotoSize.ORIGINAL)); 3367 resultUri = insertStreamItemPhoto(secondStreamItemId, photo2Values, null); 3368 long secondPhotoId = ContentUris.parseId(resultUri); 3369 photo2Values.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3370 3371 // Select the first photo. 3372 assertStoredValues(ContentUris.withAppendedId( 3373 Uri.withAppendedPath( 3374 ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 3375 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3376 firstPhotoId), 3377 photo1Values); 3378 3379 // Select the second photo. 3380 assertStoredValues(ContentUris.withAppendedId( 3381 Uri.withAppendedPath( 3382 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId), 3383 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3384 secondPhotoId), 3385 photo2Values); 3386 } 3387 3388 // Stream item insertion test cases. 3389 3390 public void testInsertStreamItemIntoOtherAccount() { 3391 long rawContactId = createRawContact(mAccount); 3392 ContentValues values = buildGenericStreamItemValues(); 3393 try { 3394 insertStreamItem(rawContactId, values, mAccountTwo); 3395 fail("Stream insertion was allowed in another account's raw contact."); 3396 } catch (SecurityException expected) { 3397 // Trying to insert stream items into account one's raw contact is forbidden. 3398 } 3399 } 3400 3401 public void testInsertStreamItemInProfileRequiresWriteProfileAccess() { 3402 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3403 3404 // With our (default) write profile permission, we should be able to insert a stream item. 3405 ContentValues values = buildGenericStreamItemValues(); 3406 insertStreamItem(profileRawContactId, values, null); 3407 3408 // Now take away write profile permission. 3409 mActor.removePermissions("android.permission.WRITE_PROFILE"); 3410 3411 // Try inserting another stream item. 3412 try { 3413 insertStreamItem(profileRawContactId, values, null); 3414 fail("Should require WRITE_PROFILE access to insert a stream item in the profile."); 3415 } catch (SecurityException expected) { 3416 // Trying to insert a stream item in the profile without WRITE_PROFILE permission 3417 // should fail. 3418 } 3419 } 3420 3421 public void testInsertStreamItemWithContentValues() { 3422 long rawContactId = createRawContact(); 3423 ContentValues values = buildGenericStreamItemValues(); 3424 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3425 mResolver.insert(StreamItems.CONTENT_URI, values); 3426 assertStoredValues(Uri.withAppendedPath( 3427 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3428 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 3429 } 3430 3431 public void testInsertStreamItemOverLimit() { 3432 long rawContactId = createRawContact(); 3433 ContentValues values = buildGenericStreamItemValues(); 3434 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3435 3436 List<Long> streamItemIds = Lists.newArrayList(); 3437 3438 // Insert MAX + 1 stream items. 3439 long baseTime = System.currentTimeMillis(); 3440 for (int i = 0; i < 6; i++) { 3441 values.put(StreamItems.TIMESTAMP, baseTime + i); 3442 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 3443 streamItemIds.add(ContentUris.parseId(resultUri)); 3444 } 3445 Long doomedStreamItemId = streamItemIds.get(0); 3446 3447 // There should only be MAX items. The oldest one should have been cleaned up. 3448 Cursor c = mResolver.query( 3449 Uri.withAppendedPath( 3450 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3451 RawContacts.StreamItems.CONTENT_DIRECTORY), 3452 new String[]{StreamItems._ID}, null, null, null); 3453 try { 3454 while(c.moveToNext()) { 3455 long streamItemId = c.getLong(0); 3456 streamItemIds.remove(streamItemId); 3457 } 3458 } finally { 3459 c.close(); 3460 } 3461 3462 assertEquals(1, streamItemIds.size()); 3463 assertEquals(doomedStreamItemId, streamItemIds.get(0)); 3464 } 3465 3466 public void testInsertStreamItemOlderThanOldestInLimit() { 3467 long rawContactId = createRawContact(); 3468 ContentValues values = buildGenericStreamItemValues(); 3469 values.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3470 3471 // Insert MAX stream items. 3472 long baseTime = System.currentTimeMillis(); 3473 for (int i = 0; i < 5; i++) { 3474 values.put(StreamItems.TIMESTAMP, baseTime + i); 3475 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 3476 assertNotSame("Expected non-0 stream item ID to be inserted", 3477 0L, ContentUris.parseId(resultUri)); 3478 } 3479 3480 // Now try to insert a stream item that's older. It should be deleted immediately 3481 // and return an ID of 0. 3482 values.put(StreamItems.TIMESTAMP, baseTime - 1); 3483 Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values); 3484 assertEquals(0L, ContentUris.parseId(resultUri)); 3485 } 3486 3487 // Stream item photo insertion test cases. 3488 3489 public void testInsertStreamItemsAndPhotosInBatch() throws Exception { 3490 long rawContactId = createRawContact(); 3491 ContentValues streamItemValues = buildGenericStreamItemValues(); 3492 ContentValues streamItemPhotoValues = buildGenericStreamItemPhotoValues(0); 3493 3494 ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); 3495 ops.add(ContentProviderOperation.newInsert( 3496 Uri.withAppendedPath( 3497 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3498 RawContacts.StreamItems.CONTENT_DIRECTORY)) 3499 .withValues(streamItemValues).build()); 3500 for (int i = 0; i < 5; i++) { 3501 streamItemPhotoValues.put(StreamItemPhotos.SORT_INDEX, i); 3502 ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI) 3503 .withValues(streamItemPhotoValues) 3504 .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, 0) 3505 .build()); 3506 } 3507 mResolver.applyBatch(ContactsContract.AUTHORITY, ops); 3508 3509 // Check that all five photos were inserted under the raw contact. 3510 Cursor c = mResolver.query(StreamItems.CONTENT_URI, new String[]{StreamItems._ID}, 3511 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)}, 3512 null); 3513 long streamItemId = 0; 3514 try { 3515 assertEquals(1, c.getCount()); 3516 c.moveToFirst(); 3517 streamItemId = c.getLong(0); 3518 } finally { 3519 c.close(); 3520 } 3521 3522 c = mResolver.query(Uri.withAppendedPath( 3523 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3524 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3525 new String[]{StreamItemPhotos._ID, StreamItemPhotos.PHOTO_URI}, 3526 null, null, null); 3527 try { 3528 assertEquals(5, c.getCount()); 3529 byte[] expectedPhotoBytes = loadPhotoFromResource( 3530 R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO); 3531 while (c.moveToNext()) { 3532 String photoUri = c.getString(1); 3533 assertInputStreamContent(expectedPhotoBytes, 3534 mResolver.openInputStream(Uri.parse(photoUri))); 3535 } 3536 } finally { 3537 c.close(); 3538 } 3539 } 3540 3541 // Stream item update test cases. 3542 3543 public void testUpdateStreamItemById() { 3544 long rawContactId = createRawContact(); 3545 ContentValues values = buildGenericStreamItemValues(); 3546 Uri resultUri = insertStreamItem(rawContactId, values, null); 3547 long streamItemId = ContentUris.parseId(resultUri); 3548 values.put(StreamItems.TEXT, "Goodbye world"); 3549 mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), values, 3550 null, null); 3551 assertStoredValues(Uri.withAppendedPath( 3552 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3553 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 3554 } 3555 3556 public void testUpdateStreamItemWithContentValues() { 3557 long rawContactId = createRawContact(); 3558 ContentValues values = buildGenericStreamItemValues(); 3559 Uri resultUri = insertStreamItem(rawContactId, values, null); 3560 long streamItemId = ContentUris.parseId(resultUri); 3561 values.put(StreamItems._ID, streamItemId); 3562 values.put(StreamItems.TEXT, "Goodbye world"); 3563 mResolver.update(StreamItems.CONTENT_URI, values, null, null); 3564 assertStoredValues(Uri.withAppendedPath( 3565 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3566 RawContacts.StreamItems.CONTENT_DIRECTORY), values); 3567 } 3568 3569 public void testUpdateStreamItemFromOtherAccount() { 3570 long rawContactId = createRawContact(mAccount); 3571 ContentValues values = buildGenericStreamItemValues(); 3572 Uri resultUri = insertStreamItem(rawContactId, values, mAccount); 3573 long streamItemId = ContentUris.parseId(resultUri); 3574 values.put(StreamItems._ID, streamItemId); 3575 values.put(StreamItems.TEXT, "Goodbye world"); 3576 try { 3577 mResolver.update(maybeAddAccountQueryParameters(StreamItems.CONTENT_URI, mAccountTwo), 3578 values, null, null); 3579 fail("Should not be able to update stream items inserted by another account"); 3580 } catch (SecurityException expected) { 3581 // Can't update the stream items from another account. 3582 } 3583 } 3584 3585 // Stream item photo update test cases. 3586 3587 public void testUpdateStreamItemPhotoById() throws IOException { 3588 long rawContactId = createRawContact(); 3589 ContentValues values = buildGenericStreamItemValues(); 3590 Uri resultUri = insertStreamItem(rawContactId, values, null); 3591 long streamItemId = ContentUris.parseId(resultUri); 3592 ContentValues photoValues = buildGenericStreamItemPhotoValues(1); 3593 resultUri = insertStreamItemPhoto(streamItemId, photoValues, null); 3594 long streamItemPhotoId = ContentUris.parseId(resultUri); 3595 3596 photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 3597 R.drawable.nebula, PhotoSize.ORIGINAL)); 3598 Uri photoUri = 3599 ContentUris.withAppendedId( 3600 Uri.withAppendedPath( 3601 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3602 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3603 streamItemPhotoId); 3604 mResolver.update(photoUri, photoValues, null, null); 3605 photoValues.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3606 assertStoredValues(photoUri, photoValues); 3607 3608 // Check that the photo stored is the expected one. 3609 String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI); 3610 assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO), 3611 mResolver.openInputStream(Uri.parse(displayPhotoUri))); 3612 } 3613 3614 public void testUpdateStreamItemPhotoWithContentValues() throws IOException { 3615 long rawContactId = createRawContact(); 3616 ContentValues values = buildGenericStreamItemValues(); 3617 Uri resultUri = insertStreamItem(rawContactId, values, null); 3618 long streamItemId = ContentUris.parseId(resultUri); 3619 ContentValues photoValues = buildGenericStreamItemPhotoValues(1); 3620 resultUri = insertStreamItemPhoto(streamItemId, photoValues, null); 3621 long streamItemPhotoId = ContentUris.parseId(resultUri); 3622 3623 photoValues.put(StreamItemPhotos._ID, streamItemPhotoId); 3624 photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 3625 R.drawable.nebula, PhotoSize.ORIGINAL)); 3626 Uri photoUri = 3627 Uri.withAppendedPath( 3628 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3629 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY); 3630 mResolver.update(photoUri, photoValues, null, null); 3631 photoValues.remove(StreamItemPhotos.PHOTO); // Removed during processing. 3632 assertStoredValues(photoUri, photoValues); 3633 3634 // Check that the photo stored is the expected one. 3635 String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI); 3636 assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO), 3637 mResolver.openInputStream(Uri.parse(displayPhotoUri))); 3638 } 3639 3640 public void testUpdateStreamItemPhotoFromOtherAccount() { 3641 long rawContactId = createRawContact(mAccount); 3642 ContentValues values = buildGenericStreamItemValues(); 3643 Uri resultUri = insertStreamItem(rawContactId, values, mAccount); 3644 long streamItemId = ContentUris.parseId(resultUri); 3645 ContentValues photoValues = buildGenericStreamItemPhotoValues(1); 3646 resultUri = insertStreamItemPhoto(streamItemId, photoValues, mAccount); 3647 long streamItemPhotoId = ContentUris.parseId(resultUri); 3648 3649 photoValues.put(StreamItemPhotos._ID, streamItemPhotoId); 3650 photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource( 3651 R.drawable.galaxy, PhotoSize.ORIGINAL)); 3652 Uri photoUri = 3653 maybeAddAccountQueryParameters( 3654 Uri.withAppendedPath( 3655 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3656 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3657 mAccountTwo); 3658 try { 3659 mResolver.update(photoUri, photoValues, null, null); 3660 fail("Should not be able to update stream item photos inserted by another account"); 3661 } catch (SecurityException expected) { 3662 // Can't update a stream item photo inserted by another account. 3663 } 3664 } 3665 3666 // Stream item deletion test cases. 3667 3668 public void testDeleteStreamItemById() { 3669 long rawContactId = createRawContact(); 3670 ContentValues firstValues = buildGenericStreamItemValues(); 3671 Uri resultUri = insertStreamItem(rawContactId, firstValues, null); 3672 long firstStreamItemId = ContentUris.parseId(resultUri); 3673 3674 ContentValues secondValues = buildGenericStreamItemValues(); 3675 secondValues.put(StreamItems.TEXT, "Goodbye world"); 3676 insertStreamItem(rawContactId, secondValues, null); 3677 3678 // Delete the first stream item. 3679 mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId), 3680 null, null); 3681 3682 // Check that only the second item remains. 3683 assertStoredValues(Uri.withAppendedPath( 3684 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3685 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues); 3686 } 3687 3688 public void testDeleteStreamItemWithSelection() { 3689 long rawContactId = createRawContact(); 3690 ContentValues firstValues = buildGenericStreamItemValues(); 3691 insertStreamItem(rawContactId, firstValues, null); 3692 3693 ContentValues secondValues = buildGenericStreamItemValues(); 3694 secondValues.put(StreamItems.TEXT, "Goodbye world"); 3695 insertStreamItem(rawContactId, secondValues, null); 3696 3697 // Delete the first stream item with a custom selection. 3698 mResolver.delete(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?", 3699 new String[]{"Hello world"}); 3700 3701 // Check that only the second item remains. 3702 assertStoredValues(Uri.withAppendedPath( 3703 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3704 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues); 3705 } 3706 3707 public void testDeleteStreamItemFromOtherAccount() { 3708 long rawContactId = createRawContact(mAccount); 3709 long streamItemId = ContentUris.parseId( 3710 insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount)); 3711 try { 3712 mResolver.delete( 3713 maybeAddAccountQueryParameters( 3714 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3715 mAccountTwo), null, null); 3716 fail("Should not be able to delete stream item inserted by another account"); 3717 } catch (SecurityException expected) { 3718 // Can't delete a stream item from another account. 3719 } 3720 } 3721 3722 // Stream item photo deletion test cases. 3723 3724 public void testDeleteStreamItemPhotoById() { 3725 long rawContactId = createRawContact(); 3726 long streamItemId = ContentUris.parseId( 3727 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3728 long streamItemPhotoId = ContentUris.parseId( 3729 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null)); 3730 mResolver.delete( 3731 ContentUris.withAppendedId( 3732 Uri.withAppendedPath( 3733 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3734 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3735 streamItemPhotoId), null, null); 3736 3737 Cursor c = mResolver.query(StreamItems.CONTENT_PHOTO_URI, 3738 new String[]{StreamItemPhotos._ID}, 3739 StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{String.valueOf(streamItemId)}, 3740 null); 3741 try { 3742 assertEquals("Expected photo to be deleted.", 0, c.getCount()); 3743 } finally { 3744 c.close(); 3745 } 3746 } 3747 3748 public void testDeleteStreamItemPhotoWithSelection() { 3749 long rawContactId = createRawContact(); 3750 long streamItemId = ContentUris.parseId( 3751 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3752 ContentValues firstPhotoValues = buildGenericStreamItemPhotoValues(0); 3753 ContentValues secondPhotoValues = buildGenericStreamItemPhotoValues(1); 3754 insertStreamItemPhoto(streamItemId, firstPhotoValues, null); 3755 firstPhotoValues.remove(StreamItemPhotos.PHOTO); // Removed while processing. 3756 insertStreamItemPhoto(streamItemId, secondPhotoValues, null); 3757 Uri photoUri = Uri.withAppendedPath( 3758 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3759 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY); 3760 mResolver.delete(photoUri, StreamItemPhotos.SORT_INDEX + "=1", null); 3761 3762 assertStoredValues(photoUri, firstPhotoValues); 3763 } 3764 3765 public void testDeleteStreamItemPhotoFromOtherAccount() { 3766 long rawContactId = createRawContact(mAccount); 3767 long streamItemId = ContentUris.parseId( 3768 insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount)); 3769 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), mAccount); 3770 try { 3771 mResolver.delete(maybeAddAccountQueryParameters( 3772 Uri.withAppendedPath( 3773 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3774 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 3775 mAccountTwo), null, null); 3776 fail("Should not be able to delete stream item photo inserted by another account"); 3777 } catch (SecurityException expected) { 3778 // Can't delete a stream item photo from another account. 3779 } 3780 } 3781 3782 public void testDeleteStreamItemsWhenRawContactDeleted() { 3783 long rawContactId = createRawContact(mAccount); 3784 Uri streamItemUri = insertStreamItem(rawContactId, 3785 buildGenericStreamItemValues(), mAccount); 3786 Uri streamItemPhotoUri = insertStreamItemPhoto(ContentUris.parseId(streamItemUri), 3787 buildGenericStreamItemPhotoValues(0), mAccount); 3788 mResolver.delete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3789 null, null); 3790 3791 ContentValues[] emptyValues = new ContentValues[0]; 3792 3793 // The stream item and its photo should be gone. 3794 assertStoredValues(streamItemUri, emptyValues); 3795 assertStoredValues(streamItemPhotoUri, emptyValues); 3796 } 3797 3798 public void testQueryStreamItemLimit() { 3799 ContentValues values = new ContentValues(); 3800 values.put(StreamItems.MAX_ITEMS, 5); 3801 assertStoredValues(StreamItems.CONTENT_LIMIT_URI, values); 3802 } 3803 3804 // Tests for inserting or updating stream items as a side-effect of making status updates 3805 // (forward-compatibility of status updates into the new social stream API). 3806 3807 public void testStreamItemInsertedOnStatusUpdate() { 3808 3809 // This method of creating a raw contact automatically inserts a status update with 3810 // the status message "hacking". 3811 ContentValues values = new ContentValues(); 3812 long rawContactId = createRawContact(values, "18004664411", 3813 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 3814 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 3815 StatusUpdates.CAPABILITY_HAS_VOICE); 3816 3817 ContentValues expectedValues = new ContentValues(); 3818 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3819 expectedValues.put(StreamItems.TEXT, "hacking"); 3820 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 3821 .appendPath(String.valueOf(rawContactId)) 3822 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 3823 expectedValues); 3824 } 3825 3826 public void testStreamItemInsertedOnStatusUpdate_HtmlQuoting() { 3827 3828 // This method of creating a raw contact automatically inserts a status update with 3829 // the status message "hacking". 3830 ContentValues values = new ContentValues(); 3831 long rawContactId = createRawContact(values, "18004664411", 3832 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 3833 StatusUpdates.CAPABILITY_HAS_VOICE); 3834 3835 // Insert a new status update for the raw contact. 3836 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com", 3837 StatusUpdates.INVISIBLE, "& <b> test '", StatusUpdates.CAPABILITY_HAS_VOICE); 3838 3839 ContentValues expectedValues = new ContentValues(); 3840 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3841 expectedValues.put(StreamItems.TEXT, "& <b> test &#39;"); 3842 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 3843 .appendPath(String.valueOf(rawContactId)) 3844 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 3845 expectedValues); 3846 } 3847 3848 public void testStreamItemUpdatedOnSecondStatusUpdate() { 3849 3850 // This method of creating a raw contact automatically inserts a status update with 3851 // the status message "hacking". 3852 ContentValues values = new ContentValues(); 3853 int chatMode = StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 3854 StatusUpdates.CAPABILITY_HAS_VOICE; 3855 long rawContactId = createRawContact(values, "18004664411", 3856 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, chatMode); 3857 3858 // Insert a new status update for the raw contact. 3859 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com", 3860 StatusUpdates.INVISIBLE, "finished hacking", chatMode); 3861 3862 ContentValues expectedValues = new ContentValues(); 3863 expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId); 3864 expectedValues.put(StreamItems.TEXT, "finished hacking"); 3865 assertStoredValues(RawContacts.CONTENT_URI.buildUpon() 3866 .appendPath(String.valueOf(rawContactId)) 3867 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(), 3868 expectedValues); 3869 } 3870 3871 public void testStreamItemReadRequiresReadSocialStreamPermission() { 3872 long rawContactId = createRawContact(); 3873 long contactId = queryContactId(rawContactId); 3874 String lookupKey = queryLookupKey(contactId); 3875 long streamItemId = ContentUris.parseId( 3876 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3877 mActor.removePermissions("android.permission.READ_SOCIAL_STREAM"); 3878 3879 // Try selecting the stream item in various ways. 3880 expectSecurityException( 3881 "Querying stream items by contact ID requires social stream read permission", 3882 Uri.withAppendedPath( 3883 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 3884 Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null); 3885 3886 expectSecurityException( 3887 "Querying stream items by lookup key requires social stream read permission", 3888 Contacts.CONTENT_LOOKUP_URI.buildUpon().appendPath(lookupKey) 3889 .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(), 3890 null, null, null, null); 3891 3892 expectSecurityException( 3893 "Querying stream items by lookup key and ID requires social stream read permission", 3894 Uri.withAppendedPath(Contacts.getLookupUri(contactId, lookupKey), 3895 Contacts.StreamItems.CONTENT_DIRECTORY), 3896 null, null, null, null); 3897 3898 expectSecurityException( 3899 "Querying stream items by raw contact ID requires social stream read permission", 3900 Uri.withAppendedPath( 3901 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3902 RawContacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null); 3903 3904 expectSecurityException( 3905 "Querying stream items by raw contact ID and stream item ID requires social " + 3906 "stream read permission", 3907 ContentUris.withAppendedId( 3908 Uri.withAppendedPath( 3909 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 3910 RawContacts.StreamItems.CONTENT_DIRECTORY), 3911 streamItemId), null, null, null, null); 3912 3913 expectSecurityException( 3914 "Querying all stream items requires social stream read permission", 3915 StreamItems.CONTENT_URI, null, null, null, null); 3916 3917 expectSecurityException( 3918 "Querying stream item by ID requires social stream read permission", 3919 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3920 null, null, null, null); 3921 } 3922 3923 public void testStreamItemPhotoReadRequiresReadSocialStreamPermission() { 3924 long rawContactId = createRawContact(); 3925 long streamItemId = ContentUris.parseId( 3926 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3927 long streamItemPhotoId = ContentUris.parseId( 3928 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null)); 3929 mActor.removePermissions("android.permission.READ_SOCIAL_STREAM"); 3930 3931 // Try selecting the stream item photo in various ways. 3932 expectSecurityException( 3933 "Querying all stream item photos requires social stream read permission", 3934 StreamItems.CONTENT_URI.buildUpon() 3935 .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY).build(), 3936 null, null, null, null); 3937 3938 expectSecurityException( 3939 "Querying all stream item photos requires social stream read permission", 3940 StreamItems.CONTENT_URI.buildUpon() 3941 .appendPath(String.valueOf(streamItemId)) 3942 .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY) 3943 .appendPath(String.valueOf(streamItemPhotoId)).build(), 3944 null, null, null, null); 3945 } 3946 3947 public void testStreamItemModificationRequiresWriteSocialStreamPermission() { 3948 long rawContactId = createRawContact(); 3949 long streamItemId = ContentUris.parseId( 3950 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3951 mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM"); 3952 3953 try { 3954 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null); 3955 fail("Should not be able to insert to stream without write social stream permission"); 3956 } catch (SecurityException expected) { 3957 } 3958 3959 try { 3960 ContentValues values = new ContentValues(); 3961 values.put(StreamItems.TEXT, "Goodbye world"); 3962 mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3963 values, null, null); 3964 fail("Should not be able to update stream without write social stream permission"); 3965 } catch (SecurityException expected) { 3966 } 3967 3968 try { 3969 mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 3970 null, null); 3971 fail("Should not be able to delete from stream without write social stream permission"); 3972 } catch (SecurityException expected) { 3973 } 3974 } 3975 3976 public void testStreamItemPhotoModificationRequiresWriteSocialStreamPermission() { 3977 long rawContactId = createRawContact(); 3978 long streamItemId = ContentUris.parseId( 3979 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null)); 3980 long streamItemPhotoId = ContentUris.parseId( 3981 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null)); 3982 mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM"); 3983 3984 Uri photoUri = StreamItems.CONTENT_URI.buildUpon() 3985 .appendPath(String.valueOf(streamItemId)) 3986 .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY) 3987 .appendPath(String.valueOf(streamItemPhotoId)).build(); 3988 3989 try { 3990 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(1), null); 3991 fail("Should not be able to insert photos without write social stream permission"); 3992 } catch (SecurityException expected) { 3993 } 3994 3995 try { 3996 ContentValues values = new ContentValues(); 3997 values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(R.drawable.galaxy, 3998 PhotoSize.ORIGINAL)); 3999 mResolver.update(photoUri, values, null, null); 4000 fail("Should not be able to update photos without write social stream permission"); 4001 } catch (SecurityException expected) { 4002 } 4003 4004 try { 4005 mResolver.delete(photoUri, null, null); 4006 fail("Should not be able to delete photos without write social stream permission"); 4007 } catch (SecurityException expected) { 4008 } 4009 } 4010 4011 public void testStatusUpdateDoesNotRequireReadOrWriteSocialStreamPermission() { 4012 int protocol1 = Im.PROTOCOL_GOOGLE_TALK; 4013 String handle1 = "test (at) gmail.com"; 4014 long rawContactId = createRawContact(); 4015 insertImHandle(rawContactId, protocol1, null, handle1); 4016 mActor.removePermissions("android.permission.READ_SOCIAL_STREAM"); 4017 mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM"); 4018 4019 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green", 4020 StatusUpdates.CAPABILITY_HAS_CAMERA); 4021 4022 mActor.addPermissions("android.permission.READ_SOCIAL_STREAM"); 4023 4024 ContentValues expectedValues = new ContentValues(); 4025 expectedValues.put(StreamItems.TEXT, "Green"); 4026 assertStoredValues(Uri.withAppendedPath( 4027 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 4028 RawContacts.StreamItems.CONTENT_DIRECTORY), expectedValues); 4029 } 4030 4031 private ContentValues buildGenericStreamItemValues() { 4032 ContentValues values = new ContentValues(); 4033 values.put(StreamItems.TEXT, "Hello world"); 4034 values.put(StreamItems.TIMESTAMP, System.currentTimeMillis()); 4035 values.put(StreamItems.COMMENTS, "Reshared by 123 others"); 4036 return values; 4037 } 4038 4039 private ContentValues buildGenericStreamItemPhotoValues(int sortIndex) { 4040 ContentValues values = new ContentValues(); 4041 values.put(StreamItemPhotos.SORT_INDEX, sortIndex); 4042 values.put(StreamItemPhotos.PHOTO, 4043 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL)); 4044 return values; 4045 } 4046 4047 public void testSingleStatusUpdateRowPerContact() { 4048 int protocol1 = Im.PROTOCOL_GOOGLE_TALK; 4049 String handle1 = "test (at) gmail.com"; 4050 4051 long rawContactId1 = createRawContact(); 4052 insertImHandle(rawContactId1, protocol1, null, handle1); 4053 4054 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green", 4055 StatusUpdates.CAPABILITY_HAS_CAMERA); 4056 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow", 4057 StatusUpdates.CAPABILITY_HAS_CAMERA); 4058 insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red", 4059 StatusUpdates.CAPABILITY_HAS_CAMERA); 4060 4061 Cursor c = queryContact(queryContactId(rawContactId1), 4062 new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS}); 4063 assertEquals(1, c.getCount()); 4064 4065 c.moveToFirst(); 4066 assertEquals(StatusUpdates.INVISIBLE, c.getInt(0)); 4067 assertEquals("Red", c.getString(1)); 4068 c.close(); 4069 } 4070 4071 private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail, 4072 String ringtone) { 4073 ContentValues values = new ContentValues(); 4074 values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail); 4075 if (ringtone != null) { 4076 values.put(Contacts.CUSTOM_RINGTONE, ringtone); 4077 } 4078 4079 final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4080 int count = mResolver.update(uri, values, null, null); 4081 assertEquals(1, count); 4082 } 4083 4084 private void updateSendToVoicemailAndRingtoneWithSelection(long contactId, 4085 boolean sendToVoicemail, String ringtone) { 4086 ContentValues values = new ContentValues(); 4087 values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail); 4088 if (ringtone != null) { 4089 values.put(Contacts.CUSTOM_RINGTONE, ringtone); 4090 } 4091 4092 int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId, 4093 null); 4094 assertEquals(1, count); 4095 } 4096 4097 private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail, 4098 String expectedRingtone) { 4099 Cursor c = queryContact(contactId); 4100 assertTrue(c.moveToNext()); 4101 int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL)); 4102 assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail); 4103 String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE)); 4104 if (expectedRingtone == null) { 4105 assertNull(ringtone); 4106 } else { 4107 assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone)); 4108 } 4109 c.close(); 4110 } 4111 4112 public void testContactVisibilityUpdateOnMembershipChange() { 4113 long rawContactId = createRawContact(mAccount); 4114 assertVisibility(rawContactId, "0"); 4115 4116 long visibleGroupId = createGroup(mAccount, "123", "Visible", 1); 4117 long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0); 4118 4119 Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId); 4120 assertVisibility(rawContactId, "1"); 4121 4122 Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId); 4123 assertVisibility(rawContactId, "1"); 4124 4125 mResolver.delete(membership1, null, null); 4126 assertVisibility(rawContactId, "0"); 4127 4128 ContentValues values = new ContentValues(); 4129 values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId); 4130 4131 mResolver.update(membership2, values, null, null); 4132 assertVisibility(rawContactId, "1"); 4133 } 4134 4135 private void assertVisibility(long rawContactId, String expectedValue) { 4136 assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId), 4137 null, Contacts.IN_VISIBLE_GROUP, expectedValue); 4138 } 4139 4140 public void testSupplyingBothValuesAndParameters() throws Exception { 4141 Account account = new Account("account 1", "type%/:1"); 4142 Uri uri = ContactsContract.Groups.CONTENT_URI.buildUpon() 4143 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_NAME, account.name) 4144 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_TYPE, account.type) 4145 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") 4146 .build(); 4147 4148 ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri); 4149 builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type); 4150 builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name); 4151 builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some id"); 4152 builder.withValue(ContactsContract.Groups.TITLE, "some name"); 4153 builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1); 4154 4155 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build())); 4156 4157 builder = ContentProviderOperation.newInsert(uri); 4158 builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type + "diff"); 4159 builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name); 4160 builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some other id"); 4161 builder.withValue(ContactsContract.Groups.TITLE, "some other name"); 4162 builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1); 4163 4164 try { 4165 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build())); 4166 fail("Expected IllegalArgumentException"); 4167 } catch (IllegalArgumentException ex) { 4168 // Expected 4169 } 4170 } 4171 4172 public void testContentEntityIterator() { 4173 // create multiple contacts and check that the selected ones are returned 4174 long id; 4175 4176 long groupId1 = createGroup(mAccount, "gsid1", "title1"); 4177 long groupId2 = createGroup(mAccount, "gsid2", "title2"); 4178 4179 id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c0"); 4180 insertGroupMembership(id, "gsid1"); 4181 insertEmail(id, "c0 (at) email.com"); 4182 insertPhoneNumber(id, "5551212c0"); 4183 4184 long c1 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c1"); 4185 Uri id_1_0 = insertGroupMembership(id, "gsid1"); 4186 Uri id_1_1 = insertGroupMembership(id, "gsid2"); 4187 Uri id_1_2 = insertEmail(id, "c1 (at) email.com"); 4188 Uri id_1_3 = insertPhoneNumber(id, "5551212c1"); 4189 4190 long c2 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c2"); 4191 Uri id_2_0 = insertGroupMembership(id, "gsid1"); 4192 Uri id_2_1 = insertEmail(id, "c2 (at) email.com"); 4193 Uri id_2_2 = insertPhoneNumber(id, "5551212c2"); 4194 4195 long c3 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c3"); 4196 Uri id_3_0 = insertGroupMembership(id, groupId2); 4197 Uri id_3_1 = insertEmail(id, "c3 (at) email.com"); 4198 Uri id_3_2 = insertPhoneNumber(id, "5551212c3"); 4199 4200 EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query( 4201 maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), null, 4202 RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null)); 4203 Entity entity; 4204 ContentValues[] subValues; 4205 entity = iterator.next(); 4206 assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 4207 subValues = asSortedContentValuesArray(entity.getSubValues()); 4208 assertEquals(4, subValues.length); 4209 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 4210 Data._ID, id_1_0, 4211 GroupMembership.GROUP_ROW_ID, groupId1, 4212 GroupMembership.GROUP_SOURCE_ID, "gsid1"); 4213 assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE, 4214 Data._ID, id_1_1, 4215 GroupMembership.GROUP_ROW_ID, groupId2, 4216 GroupMembership.GROUP_SOURCE_ID, "gsid2"); 4217 assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE, 4218 Data._ID, id_1_2, 4219 Email.DATA, "c1 (at) email.com"); 4220 assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE, 4221 Data._ID, id_1_3, 4222 Email.DATA, "5551212c1"); 4223 4224 entity = iterator.next(); 4225 assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 4226 subValues = asSortedContentValuesArray(entity.getSubValues()); 4227 assertEquals(3, subValues.length); 4228 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 4229 Data._ID, id_2_0, 4230 GroupMembership.GROUP_ROW_ID, groupId1, 4231 GroupMembership.GROUP_SOURCE_ID, "gsid1"); 4232 assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE, 4233 Data._ID, id_2_1, 4234 Email.DATA, "c2 (at) email.com"); 4235 assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE, 4236 Data._ID, id_2_2, 4237 Email.DATA, "5551212c2"); 4238 4239 entity = iterator.next(); 4240 assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID)); 4241 subValues = asSortedContentValuesArray(entity.getSubValues()); 4242 assertEquals(3, subValues.length); 4243 assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE, 4244 Data._ID, id_3_0, 4245 GroupMembership.GROUP_ROW_ID, groupId2, 4246 GroupMembership.GROUP_SOURCE_ID, "gsid2"); 4247 assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE, 4248 Data._ID, id_3_1, 4249 Email.DATA, "c3 (at) email.com"); 4250 assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE, 4251 Data._ID, id_3_2, 4252 Email.DATA, "5551212c3"); 4253 4254 assertFalse(iterator.hasNext()); 4255 iterator.close(); 4256 } 4257 4258 public void testDataCreateUpdateDeleteByMimeType() throws Exception { 4259 long rawContactId = createRawContact(); 4260 4261 ContentValues values = new ContentValues(); 4262 values.put(Data.RAW_CONTACT_ID, rawContactId); 4263 values.put(Data.MIMETYPE, "testmimetype"); 4264 values.put(Data.RES_PACKAGE, "oldpackage"); 4265 values.put(Data.IS_PRIMARY, 1); 4266 values.put(Data.IS_SUPER_PRIMARY, 1); 4267 values.put(Data.DATA1, "old1"); 4268 values.put(Data.DATA2, "old2"); 4269 values.put(Data.DATA3, "old3"); 4270 values.put(Data.DATA4, "old4"); 4271 values.put(Data.DATA5, "old5"); 4272 values.put(Data.DATA6, "old6"); 4273 values.put(Data.DATA7, "old7"); 4274 values.put(Data.DATA8, "old8"); 4275 values.put(Data.DATA9, "old9"); 4276 values.put(Data.DATA10, "old10"); 4277 values.put(Data.DATA11, "old11"); 4278 values.put(Data.DATA12, "old12"); 4279 values.put(Data.DATA13, "old13"); 4280 values.put(Data.DATA14, "old14"); 4281 values.put(Data.DATA15, "old15"); 4282 Uri uri = mResolver.insert(Data.CONTENT_URI, values); 4283 assertStoredValues(uri, values); 4284 assertNetworkNotified(true); 4285 4286 values.clear(); 4287 values.put(Data.RES_PACKAGE, "newpackage"); 4288 values.put(Data.IS_PRIMARY, 0); 4289 values.put(Data.IS_SUPER_PRIMARY, 0); 4290 values.put(Data.DATA1, "new1"); 4291 values.put(Data.DATA2, "new2"); 4292 values.put(Data.DATA3, "new3"); 4293 values.put(Data.DATA4, "new4"); 4294 values.put(Data.DATA5, "new5"); 4295 values.put(Data.DATA6, "new6"); 4296 values.put(Data.DATA7, "new7"); 4297 values.put(Data.DATA8, "new8"); 4298 values.put(Data.DATA9, "new9"); 4299 values.put(Data.DATA10, "new10"); 4300 values.put(Data.DATA11, "new11"); 4301 values.put(Data.DATA12, "new12"); 4302 values.put(Data.DATA13, "new13"); 4303 values.put(Data.DATA14, "new14"); 4304 values.put(Data.DATA15, "new15"); 4305 mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId + 4306 " AND " + Data.MIMETYPE + "='testmimetype'", null); 4307 assertNetworkNotified(true); 4308 4309 assertStoredValues(uri, values); 4310 4311 int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId 4312 + " AND " + Data.MIMETYPE + "='testmimetype'", null); 4313 assertEquals(1, count); 4314 assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId 4315 + " AND " + Data.MIMETYPE + "='testmimetype'", null)); 4316 assertNetworkNotified(true); 4317 } 4318 4319 public void testRawContactQuery() { 4320 Account account1 = new Account("a", "b"); 4321 Account account2 = new Account("c", "d"); 4322 long rawContactId1 = createRawContact(account1); 4323 long rawContactId2 = createRawContact(account2); 4324 4325 Uri uri1 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1); 4326 Uri uri2 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2); 4327 assertEquals(1, getCount(uri1, null, null)); 4328 assertEquals(1, getCount(uri2, null, null)); 4329 assertStoredValue(uri1, RawContacts._ID, rawContactId1) ; 4330 assertStoredValue(uri2, RawContacts._ID, rawContactId2) ; 4331 4332 Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1); 4333 Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2); 4334 assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ; 4335 assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ; 4336 } 4337 4338 public void testRawContactDeletion() { 4339 long rawContactId = createRawContact(mAccount); 4340 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4341 4342 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com"); 4343 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com", 4344 StatusUpdates.AVAILABLE, null, 4345 StatusUpdates.CAPABILITY_HAS_CAMERA); 4346 long contactId = queryContactId(rawContactId); 4347 4348 assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 4349 null, null)); 4350 assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 4351 + rawContactId, null)); 4352 4353 mResolver.delete(uri, null, null); 4354 4355 assertStoredValue(uri, RawContacts.DELETED, "1"); 4356 assertNetworkNotified(true); 4357 4358 Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount); 4359 mResolver.delete(permanentDeletionUri, null, null); 4360 assertEquals(0, getCount(uri, null, null)); 4361 assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 4362 null, null)); 4363 assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 4364 + rawContactId, null)); 4365 assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null)); 4366 assertNetworkNotified(false); 4367 } 4368 4369 public void testRawContactDeletionKeepingAggregateContact() { 4370 long rawContactId1 = createRawContactWithName(mAccount); 4371 long rawContactId2 = createRawContactWithName(mAccount); 4372 setAggregationException( 4373 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 4374 4375 long contactId = queryContactId(rawContactId1); 4376 4377 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 4378 Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount); 4379 mResolver.delete(permanentDeletionUri, null, null); 4380 assertEquals(0, getCount(uri, null, null)); 4381 assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null)); 4382 } 4383 4384 public void testRawContactDeletionWithAccounts() { 4385 long rawContactId = createRawContact(mAccount); 4386 Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4387 4388 insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com"); 4389 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com", 4390 StatusUpdates.AVAILABLE, null, 4391 StatusUpdates.CAPABILITY_HAS_CAMERA); 4392 assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY), 4393 null, null)); 4394 assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 4395 + rawContactId, null)); 4396 4397 // Do not delete if we are deleting with wrong account. 4398 Uri deleteWithWrongAccountUri = 4399 RawContacts.CONTENT_URI.buildUpon() 4400 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name) 4401 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type) 4402 .build(); 4403 mResolver.delete(deleteWithWrongAccountUri, null, null); 4404 4405 assertStoredValue(uri, RawContacts.DELETED, "0"); 4406 4407 // Delete if we are deleting with correct account. 4408 Uri deleteWithCorrectAccountUri = 4409 RawContacts.CONTENT_URI.buildUpon() 4410 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name) 4411 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type) 4412 .build(); 4413 mResolver.delete(deleteWithCorrectAccountUri, null, null); 4414 4415 assertStoredValue(uri, RawContacts.DELETED, "1"); 4416 } 4417 4418 public void testAccountsUpdated() { 4419 // This is to ensure we do not delete contacts with null, null (account name, type) 4420 // accidentally. 4421 long rawContactId3 = createRawContactWithName("James", "Sullivan"); 4422 insertPhoneNumber(rawContactId3, "5234567890"); 4423 Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3); 4424 assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null)); 4425 4426 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 4427 mActor.setAccounts(new Account[]{mAccount, mAccountTwo}); 4428 cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo}); 4429 assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null)); 4430 assertStoredValue(rawContact3, RawContacts.ACCOUNT_NAME, null); 4431 assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, null); 4432 4433 long rawContactId1 = createRawContact(mAccount); 4434 insertEmail(rawContactId1, "account1 (at) email.com"); 4435 long rawContactId2 = createRawContact(mAccountTwo); 4436 insertEmail(rawContactId2, "account2 (at) email.com"); 4437 insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com"); 4438 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com", 4439 StatusUpdates.AVAILABLE, null, 4440 StatusUpdates.CAPABILITY_HAS_CAMERA); 4441 4442 mActor.setAccounts(new Account[]{mAccount}); 4443 cp.onAccountsUpdated(new Account[]{mAccount}); 4444 assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null)); 4445 assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "=" 4446 + rawContactId2, null)); 4447 } 4448 4449 public void testAccountDeletion() { 4450 Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE); 4451 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 4452 mActor.setAccounts(new Account[]{readOnlyAccount, mAccount}); 4453 cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount}); 4454 4455 long rawContactId1 = createRawContactWithName("John", "Doe", readOnlyAccount); 4456 Uri photoUri1 = insertPhoto(rawContactId1); 4457 long rawContactId2 = createRawContactWithName("john", "doe", mAccount); 4458 Uri photoUri2 = insertPhoto(rawContactId2); 4459 storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1"); 4460 4461 assertAggregated(rawContactId1, rawContactId2); 4462 4463 long contactId = queryContactId(rawContactId1); 4464 4465 // The display name should come from the writable account 4466 assertStoredValue(Uri.withAppendedPath( 4467 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4468 Contacts.Data.CONTENT_DIRECTORY), 4469 Contacts.DISPLAY_NAME, "john doe"); 4470 4471 // The photo should be the one we marked as super-primary 4472 assertStoredValue(Contacts.CONTENT_URI, contactId, 4473 Contacts.PHOTO_ID, ContentUris.parseId(photoUri2)); 4474 4475 mActor.setAccounts(new Account[]{readOnlyAccount}); 4476 // Remove the writable account 4477 cp.onAccountsUpdated(new Account[]{readOnlyAccount}); 4478 4479 // The display name should come from the remaining account 4480 assertStoredValue(Uri.withAppendedPath( 4481 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4482 Contacts.Data.CONTENT_DIRECTORY), 4483 Contacts.DISPLAY_NAME, "John Doe"); 4484 4485 // The photo should be the remaining one 4486 assertStoredValue(Contacts.CONTENT_URI, contactId, 4487 Contacts.PHOTO_ID, ContentUris.parseId(photoUri1)); 4488 } 4489 4490 public void testStreamItemsCleanedUpOnAccountRemoval() { 4491 Account doomedAccount = new Account("doom", "doom"); 4492 Account safeAccount = mAccount; 4493 ContactsProvider2 cp = (ContactsProvider2) getProvider(); 4494 mActor.setAccounts(new Account[]{doomedAccount, safeAccount}); 4495 cp.onAccountsUpdated(new Account[]{doomedAccount, safeAccount}); 4496 4497 // Create a doomed raw contact, stream item, and photo. 4498 long doomedRawContactId = createRawContactWithName(doomedAccount); 4499 Uri doomedStreamItemUri = 4500 insertStreamItem(doomedRawContactId, buildGenericStreamItemValues(), doomedAccount); 4501 long doomedStreamItemId = ContentUris.parseId(doomedStreamItemUri); 4502 Uri doomedStreamItemPhotoUri = insertStreamItemPhoto( 4503 doomedStreamItemId, buildGenericStreamItemPhotoValues(0), doomedAccount); 4504 4505 // Create a safe raw contact, stream item, and photo. 4506 long safeRawContactId = createRawContactWithName(safeAccount); 4507 Uri safeStreamItemUri = 4508 insertStreamItem(safeRawContactId, buildGenericStreamItemValues(), safeAccount); 4509 long safeStreamItemId = ContentUris.parseId(safeStreamItemUri); 4510 Uri safeStreamItemPhotoUri = insertStreamItemPhoto( 4511 safeStreamItemId, buildGenericStreamItemPhotoValues(0), safeAccount); 4512 long safeStreamItemPhotoId = ContentUris.parseId(safeStreamItemPhotoUri); 4513 4514 // Remove the doomed account. 4515 mActor.setAccounts(new Account[]{safeAccount}); 4516 cp.onAccountsUpdated(new Account[]{safeAccount}); 4517 4518 // Check that the doomed stuff has all been nuked. 4519 ContentValues[] noValues = new ContentValues[0]; 4520 assertStoredValues(ContentUris.withAppendedId(RawContacts.CONTENT_URI, doomedRawContactId), 4521 noValues); 4522 assertStoredValues(doomedStreamItemUri, noValues); 4523 assertStoredValues(doomedStreamItemPhotoUri, noValues); 4524 4525 // Check that the safe stuff lives on. 4526 assertStoredValue(RawContacts.CONTENT_URI, safeRawContactId, RawContacts._ID, 4527 safeRawContactId); 4528 assertStoredValue(safeStreamItemUri, StreamItems._ID, safeStreamItemId); 4529 assertStoredValue(safeStreamItemPhotoUri, StreamItemPhotos._ID, safeStreamItemPhotoId); 4530 } 4531 4532 public void testContactDeletion() { 4533 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 4534 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2); 4535 4536 long contactId = queryContactId(rawContactId1); 4537 4538 mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null); 4539 4540 assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1), 4541 RawContacts.DELETED, "1"); 4542 assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2), 4543 RawContacts.DELETED, "1"); 4544 } 4545 4546 public void testMarkAsDirtyParameter() { 4547 long rawContactId = createRawContact(mAccount); 4548 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 4549 4550 Uri uri = insertStructuredName(rawContactId, "John", "Doe"); 4551 clearDirty(rawContactUri); 4552 Uri updateUri = setCallerIsSyncAdapter(uri, mAccount); 4553 4554 ContentValues values = new ContentValues(); 4555 values.put(StructuredName.FAMILY_NAME, "Dough"); 4556 mResolver.update(updateUri, values, null, null); 4557 assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough"); 4558 assertDirty(rawContactUri, false); 4559 assertNetworkNotified(false); 4560 } 4561 4562 public void testRawContactDirtyAndVersion() { 4563 final long rawContactId = createRawContact(mAccount); 4564 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId); 4565 assertDirty(uri, false); 4566 long version = getVersion(uri); 4567 4568 ContentValues values = new ContentValues(); 4569 values.put(ContactsContract.RawContacts.DIRTY, 0); 4570 values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1); 4571 values.put(ContactsContract.RawContacts.AGGREGATION_MODE, 4572 RawContacts.AGGREGATION_MODE_IMMEDIATE); 4573 values.put(ContactsContract.RawContacts.STARRED, 1); 4574 assertEquals(1, mResolver.update(uri, values, null, null)); 4575 assertEquals(version, getVersion(uri)); 4576 4577 assertDirty(uri, false); 4578 assertNetworkNotified(false); 4579 4580 Uri emailUri = insertEmail(rawContactId, "goo (at) woo.com"); 4581 assertDirty(uri, true); 4582 assertNetworkNotified(true); 4583 ++version; 4584 assertEquals(version, getVersion(uri)); 4585 clearDirty(uri); 4586 4587 values = new ContentValues(); 4588 values.put(Email.DATA, "goo (at) hoo.com"); 4589 mResolver.update(emailUri, values, null, null); 4590 assertDirty(uri, true); 4591 assertNetworkNotified(true); 4592 ++version; 4593 assertEquals(version, getVersion(uri)); 4594 clearDirty(uri); 4595 4596 mResolver.delete(emailUri, null, null); 4597 assertDirty(uri, true); 4598 assertNetworkNotified(true); 4599 ++version; 4600 assertEquals(version, getVersion(uri)); 4601 } 4602 4603 public void testRawContactClearDirty() { 4604 final long rawContactId = createRawContact(mAccount); 4605 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, 4606 rawContactId); 4607 long version = getVersion(uri); 4608 insertEmail(rawContactId, "goo (at) woo.com"); 4609 assertDirty(uri, true); 4610 version++; 4611 assertEquals(version, getVersion(uri)); 4612 4613 clearDirty(uri); 4614 assertDirty(uri, false); 4615 assertEquals(version, getVersion(uri)); 4616 } 4617 4618 public void testRawContactDeletionSetsDirty() { 4619 final long rawContactId = createRawContact(mAccount); 4620 Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, 4621 rawContactId); 4622 long version = getVersion(uri); 4623 clearDirty(uri); 4624 assertDirty(uri, false); 4625 4626 mResolver.delete(uri, null, null); 4627 assertStoredValue(uri, RawContacts.DELETED, "1"); 4628 assertDirty(uri, true); 4629 assertNetworkNotified(true); 4630 version++; 4631 assertEquals(version, getVersion(uri)); 4632 } 4633 4634 public void testDeleteContactWithoutName() { 4635 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 4636 long rawContactId = ContentUris.parseId(rawContactUri); 4637 4638 Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true); 4639 4640 long contactId = queryContactId(rawContactId); 4641 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4642 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 4643 4644 int numDeleted = mResolver.delete(lookupUri, null, null); 4645 assertEquals(1, numDeleted); 4646 } 4647 4648 public void testDeleteContactWithoutAnyData() { 4649 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 4650 long rawContactId = ContentUris.parseId(rawContactUri); 4651 4652 long contactId = queryContactId(rawContactId); 4653 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4654 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 4655 4656 int numDeleted = mResolver.delete(lookupUri, null, null); 4657 assertEquals(1, numDeleted); 4658 } 4659 4660 public void testDeleteContactWithEscapedUri() { 4661 ContentValues values = new ContentValues(); 4662 values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~"); 4663 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4664 long rawContactId = ContentUris.parseId(rawContactUri); 4665 4666 long contactId = queryContactId(rawContactId); 4667 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4668 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 4669 assertEquals(1, mResolver.delete(lookupUri, null, null)); 4670 } 4671 4672 public void testQueryContactWithEscapedUri() { 4673 ContentValues values = new ContentValues(); 4674 values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~"); 4675 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4676 long rawContactId = ContentUris.parseId(rawContactUri); 4677 4678 long contactId = queryContactId(rawContactId); 4679 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4680 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 4681 Cursor c = mResolver.query(lookupUri, null, null, null, ""); 4682 assertEquals(1, c.getCount()); 4683 c.close(); 4684 } 4685 4686 public void testGetPhotoUri() { 4687 ContentValues values = new ContentValues(); 4688 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4689 long rawContactId = ContentUris.parseId(rawContactUri); 4690 insertStructuredName(rawContactId, "John", "Doe"); 4691 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 4692 long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?", 4693 new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID); 4694 String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId) 4695 .toString(); 4696 4697 assertStoredValue( 4698 ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)), 4699 Contacts.PHOTO_URI, photoUri); 4700 } 4701 4702 public void testGetPhotoViaLookupUri() throws IOException { 4703 long rawContactId = createRawContact(); 4704 long contactId = queryContactId(rawContactId); 4705 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4706 Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri); 4707 String lookupKey = lookupUri.getPathSegments().get(2); 4708 insertPhoto(rawContactId, R.drawable.earth_small); 4709 byte[] thumbnail = loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL); 4710 4711 // Two forms of lookup key URIs should be valid - one with the contact ID, one without. 4712 Uri photoLookupUriWithId = Uri.withAppendedPath(lookupUri, "photo"); 4713 Uri photoLookupUriWithoutId = Contacts.CONTENT_LOOKUP_URI.buildUpon() 4714 .appendPath(lookupKey).appendPath("photo").build(); 4715 4716 // Try retrieving as a data record. 4717 ContentValues values = new ContentValues(); 4718 values.put(Photo.PHOTO, thumbnail); 4719 assertStoredValues(photoLookupUriWithId, values); 4720 assertStoredValues(photoLookupUriWithoutId, values); 4721 4722 // Try opening as an input stream. 4723 assertInputStreamContent(thumbnail, mResolver.openInputStream(photoLookupUriWithId)); 4724 assertInputStreamContent(thumbnail, mResolver.openInputStream(photoLookupUriWithoutId)); 4725 } 4726 4727 public void testInputStreamForPhoto() throws Exception { 4728 long rawContactId = createRawContact(); 4729 long contactId = queryContactId(rawContactId); 4730 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 4731 insertPhoto(rawContactId); 4732 Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI)); 4733 Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI)); 4734 4735 assertInputStreamContent(loadTestPhoto(PhotoSize.DISPLAY_PHOTO), 4736 mResolver.openInputStream(photoUri)); 4737 assertInputStreamContent(loadTestPhoto(PhotoSize.THUMBNAIL), 4738 mResolver.openInputStream(photoThumbnailUri)); 4739 } 4740 4741 private static void assertInputStreamContent(byte[] expected, InputStream is) 4742 throws IOException { 4743 try { 4744 byte[] observed = new byte[expected.length]; 4745 int count = is.read(observed); 4746 assertEquals(expected.length, count); 4747 assertEquals(-1, is.read()); 4748 MoreAsserts.assertEquals(expected, observed); 4749 } finally { 4750 is.close(); 4751 } 4752 } 4753 4754 public void testSuperPrimaryPhoto() { 4755 long rawContactId1 = createRawContact(new Account("a", "a")); 4756 Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal); 4757 long photoId1 = ContentUris.parseId(photoUri1); 4758 4759 long rawContactId2 = createRawContact(new Account("b", "b")); 4760 Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal); 4761 long photoId2 = ContentUris.parseId(photoUri2); 4762 4763 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 4764 rawContactId1, rawContactId2); 4765 4766 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 4767 queryContactId(rawContactId1)); 4768 4769 long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?", 4770 new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID); 4771 String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1) 4772 .toString(); 4773 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1); 4774 assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri); 4775 4776 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 4777 rawContactId1, rawContactId2); 4778 4779 ContentValues values = new ContentValues(); 4780 values.put(Data.IS_SUPER_PRIMARY, 1); 4781 mResolver.update(photoUri2, values, null, null); 4782 4783 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 4784 rawContactId1, rawContactId2); 4785 contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 4786 queryContactId(rawContactId1)); 4787 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2); 4788 4789 mResolver.update(photoUri1, values, null, null); 4790 assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1); 4791 } 4792 4793 public void testUpdatePhoto() { 4794 ContentValues values = new ContentValues(); 4795 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4796 long rawContactId = ContentUris.parseId(rawContactUri); 4797 insertStructuredName(rawContactId, "John", "Doe"); 4798 4799 Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 4800 queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY); 4801 4802 values.clear(); 4803 values.put(Data.RAW_CONTACT_ID, rawContactId); 4804 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 4805 values.putNull(Photo.PHOTO); 4806 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 4807 long photoId = ContentUris.parseId(dataUri); 4808 4809 assertEquals(0, getCount(twigUri, null, null)); 4810 4811 values.clear(); 4812 values.put(Photo.PHOTO, loadTestPhoto()); 4813 mResolver.update(dataUri, values, null, null); 4814 assertNetworkNotified(true); 4815 4816 long twigId = getStoredLongValue(twigUri, Data._ID); 4817 assertEquals(photoId, twigId); 4818 } 4819 4820 public void testUpdateRawContactDataPhoto() { 4821 // setup a contact with a null photo 4822 ContentValues values = new ContentValues(); 4823 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 4824 long rawContactId = ContentUris.parseId(rawContactUri); 4825 4826 // setup a photo 4827 values.put(Data.RAW_CONTACT_ID, rawContactId); 4828 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 4829 values.putNull(Photo.PHOTO); 4830 4831 // try to do an update before insert should return count == 0 4832 Uri dataUri = Uri.withAppendedPath( 4833 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 4834 RawContacts.Data.CONTENT_DIRECTORY); 4835 assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?", 4836 new String[] {Photo.CONTENT_ITEM_TYPE})); 4837 4838 mResolver.insert(Data.CONTENT_URI, values); 4839 4840 // save a photo to the db 4841 values.clear(); 4842 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 4843 values.put(Photo.PHOTO, loadTestPhoto()); 4844 assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?", 4845 new String[] {Photo.CONTENT_ITEM_TYPE})); 4846 4847 // verify the photo 4848 Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO}, 4849 Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null); 4850 storedPhoto.moveToFirst(); 4851 MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0)); 4852 storedPhoto.close(); 4853 } 4854 4855 public void testOpenDisplayPhotoForContactId() throws IOException { 4856 long rawContactId = createRawContactWithName(); 4857 long contactId = queryContactId(rawContactId); 4858 insertPhoto(rawContactId, R.drawable.earth_normal); 4859 Uri photoUri = Contacts.CONTENT_URI.buildUpon() 4860 .appendPath(String.valueOf(contactId)) 4861 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 4862 assertInputStreamContent( 4863 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4864 mResolver.openInputStream(photoUri)); 4865 } 4866 4867 public void testOpenDisplayPhotoForContactLookupKey() throws IOException { 4868 long rawContactId = createRawContactWithName(); 4869 long contactId = queryContactId(rawContactId); 4870 String lookupKey = queryLookupKey(contactId); 4871 insertPhoto(rawContactId, R.drawable.earth_normal); 4872 Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon() 4873 .appendPath(lookupKey) 4874 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 4875 assertInputStreamContent( 4876 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4877 mResolver.openInputStream(photoUri)); 4878 } 4879 4880 public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException { 4881 long rawContactId = createRawContactWithName(); 4882 long contactId = queryContactId(rawContactId); 4883 String lookupKey = queryLookupKey(contactId); 4884 insertPhoto(rawContactId, R.drawable.earth_normal); 4885 Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon() 4886 .appendPath(lookupKey) 4887 .appendPath(String.valueOf(contactId)) 4888 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build(); 4889 assertInputStreamContent( 4890 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4891 mResolver.openInputStream(photoUri)); 4892 } 4893 4894 public void testOpenDisplayPhotoForRawContactId() throws IOException { 4895 long rawContactId = createRawContactWithName(); 4896 insertPhoto(rawContactId, R.drawable.earth_normal); 4897 Uri photoUri = RawContacts.CONTENT_URI.buildUpon() 4898 .appendPath(String.valueOf(rawContactId)) 4899 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 4900 assertInputStreamContent( 4901 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4902 mResolver.openInputStream(photoUri)); 4903 } 4904 4905 public void testOpenDisplayPhotoByPhotoUri() throws IOException { 4906 long rawContactId = createRawContactWithName(); 4907 long contactId = queryContactId(rawContactId); 4908 insertPhoto(rawContactId, R.drawable.earth_normal); 4909 4910 // Get the photo URI out and check the content. 4911 String photoUri = getStoredValue( 4912 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4913 Contacts.PHOTO_URI); 4914 assertInputStreamContent( 4915 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 4916 mResolver.openInputStream(Uri.parse(photoUri))); 4917 } 4918 4919 public void testPhotoUriForDisplayPhoto() { 4920 long rawContactId = createRawContactWithName(); 4921 long contactId = queryContactId(rawContactId); 4922 4923 // Photo being inserted is larger than a thumbnail, so it will be stored as a file. 4924 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 4925 String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), 4926 Photo.PHOTO_FILE_ID); 4927 String photoUri = getStoredValue( 4928 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4929 Contacts.PHOTO_URI); 4930 4931 // Check that the photo URI differs from the thumbnail. 4932 String thumbnailUri = getStoredValue( 4933 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4934 Contacts.PHOTO_THUMBNAIL_URI); 4935 assertFalse(photoUri.equals(thumbnailUri)); 4936 4937 // URI should be of the form display_photo/ID 4938 assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(), 4939 photoUri); 4940 } 4941 4942 public void testPhotoUriForThumbnailPhoto() throws IOException { 4943 long rawContactId = createRawContactWithName(); 4944 long contactId = queryContactId(rawContactId); 4945 4946 // Photo being inserted is a thumbnail, so it will only be stored in a BLOB. The photo URI 4947 // will fall back to the thumbnail URI. 4948 insertPhoto(rawContactId, R.drawable.earth_small); 4949 String photoUri = getStoredValue( 4950 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4951 Contacts.PHOTO_URI); 4952 4953 // Check that the photo URI is equal to the thumbnail URI. 4954 String thumbnailUri = getStoredValue( 4955 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4956 Contacts.PHOTO_THUMBNAIL_URI); 4957 assertEquals(photoUri, thumbnailUri); 4958 4959 // URI should be of the form contacts/ID/photo 4960 assertEquals(Uri.withAppendedPath( 4961 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4962 Contacts.Photo.CONTENT_DIRECTORY).toString(), 4963 photoUri); 4964 4965 // Loading the photo URI content should get the thumbnail. 4966 assertInputStreamContent( 4967 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL), 4968 mResolver.openInputStream(Uri.parse(photoUri))); 4969 } 4970 4971 public void testWriteNewPhotoToAssetFile() throws Exception { 4972 long rawContactId = createRawContactWithName(); 4973 long contactId = queryContactId(rawContactId); 4974 4975 // Load in a huge photo. 4976 final byte[] originalPhoto = loadPhotoFromResource( 4977 R.drawable.earth_huge, PhotoSize.ORIGINAL); 4978 4979 // Write it out. 4980 final Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon() 4981 .appendPath(String.valueOf(rawContactId)) 4982 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 4983 writePhotoAsync(writeablePhotoUri, originalPhoto); 4984 4985 // Check that the display photo and thumbnail have been set. 4986 String photoUri = null; 4987 for (int i = 0; i < 10 && photoUri == null; i++) { 4988 // Wait a tick for the photo processing to occur. 4989 Thread.sleep(100); 4990 photoUri = getStoredValue( 4991 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4992 Contacts.PHOTO_URI); 4993 } 4994 4995 assertFalse(TextUtils.isEmpty(photoUri)); 4996 String thumbnailUri = getStoredValue( 4997 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 4998 Contacts.PHOTO_THUMBNAIL_URI); 4999 assertFalse(TextUtils.isEmpty(thumbnailUri)); 5000 assertNotSame(photoUri, thumbnailUri); 5001 5002 // Check the content of the display photo and thumbnail. 5003 assertInputStreamContent( 5004 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO), 5005 mResolver.openInputStream(Uri.parse(photoUri))); 5006 assertInputStreamContent( 5007 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL), 5008 mResolver.openInputStream(Uri.parse(thumbnailUri))); 5009 } 5010 5011 public void testWriteUpdatedPhotoToAssetFile() throws Exception { 5012 long rawContactId = createRawContactWithName(); 5013 long contactId = queryContactId(rawContactId); 5014 5015 // Insert a large photo first. 5016 insertPhoto(rawContactId, R.drawable.earth_large); 5017 String largeEarthPhotoUri = getStoredValue( 5018 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI); 5019 5020 // Load in a huge photo. 5021 byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL); 5022 5023 // Write it out. 5024 Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon() 5025 .appendPath(String.valueOf(rawContactId)) 5026 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build(); 5027 writePhotoAsync(writeablePhotoUri, originalPhoto); 5028 5029 // Allow a second for processing to occur. 5030 Thread.sleep(1000); 5031 5032 // Check that the display photo URI has been modified. 5033 String hugeEarthPhotoUri = getStoredValue( 5034 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI); 5035 assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri)); 5036 5037 // Check the content of the display photo and thumbnail. 5038 String hugeEarthThumbnailUri = getStoredValue( 5039 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 5040 Contacts.PHOTO_THUMBNAIL_URI); 5041 assertInputStreamContent( 5042 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO), 5043 mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri))); 5044 assertInputStreamContent( 5045 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL), 5046 mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri))); 5047 5048 } 5049 5050 private void writePhotoAsync(final Uri uri, final byte[] photoBytes) throws Exception { 5051 AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() { 5052 @Override 5053 protected Object doInBackground(Object... params) { 5054 OutputStream os; 5055 try { 5056 os = mResolver.openOutputStream(uri, "rw"); 5057 os.write(photoBytes); 5058 os.close(); 5059 return null; 5060 } catch (IOException ioe) { 5061 throw new RuntimeException(ioe); 5062 } 5063 } 5064 }; 5065 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null).get(); 5066 } 5067 5068 public void testPhotoDimensionLimits() { 5069 ContentValues values = new ContentValues(); 5070 values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256); 5071 values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96); 5072 assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values); 5073 } 5074 5075 public void testPhotoStoreCleanup() throws IOException { 5076 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 5077 PhotoStore photoStore = provider.getPhotoStore(); 5078 5079 // Trigger an initial cleanup so another one won't happen while we're running this test. 5080 provider.cleanupPhotoStore(); 5081 5082 // Insert a couple of contacts with photos. 5083 long rawContactId1 = createRawContactWithName(); 5084 long contactId1 = queryContactId(rawContactId1); 5085 long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal)); 5086 long photoFileId1 = 5087 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1), 5088 Photo.PHOTO_FILE_ID); 5089 5090 long rawContactId2 = createRawContactWithName(); 5091 long contactId2 = queryContactId(rawContactId2); 5092 long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal)); 5093 long photoFileId2 = 5094 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2), 5095 Photo.PHOTO_FILE_ID); 5096 5097 // Update the second raw contact with a different photo. 5098 ContentValues values = new ContentValues(); 5099 values.put(Data.RAW_CONTACT_ID, rawContactId2); 5100 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 5101 values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL)); 5102 assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?", 5103 new String[]{String.valueOf(dataId2)})); 5104 long replacementPhotoFileId = 5105 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2), 5106 Photo.PHOTO_FILE_ID); 5107 5108 // Insert a third raw contact that has a bogus photo file ID. 5109 long bogusFileId = 1234567; 5110 long rawContactId3 = createRawContactWithName(); 5111 long contactId3 = queryContactId(rawContactId3); 5112 values.clear(); 5113 values.put(Data.RAW_CONTACT_ID, rawContactId3); 5114 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 5115 values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal, 5116 PhotoSize.THUMBNAIL)); 5117 values.put(Photo.PHOTO_FILE_ID, bogusFileId); 5118 values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true); 5119 mResolver.insert(Data.CONTENT_URI, values); 5120 5121 // Insert a fourth raw contact with a stream item that has a photo, then remove that photo 5122 // from the photo store. 5123 Account socialAccount = new Account("social", "social"); 5124 long rawContactId4 = createRawContactWithName(socialAccount); 5125 Uri streamItemUri = 5126 insertStreamItem(rawContactId4, buildGenericStreamItemValues(), socialAccount); 5127 long streamItemId = ContentUris.parseId(streamItemUri); 5128 Uri streamItemPhotoUri = insertStreamItemPhoto( 5129 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount); 5130 long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri, 5131 StreamItemPhotos.PHOTO_FILE_ID); 5132 photoStore.remove(streamItemPhotoFileId); 5133 5134 // Also insert a bogus photo that nobody is using. 5135 long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource( 5136 R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96)); 5137 5138 // Manually trigger another cleanup in the provider. 5139 provider.cleanupPhotoStore(); 5140 5141 // The following things should have happened. 5142 5143 // 1. Raw contact 1 and its photo remain unaffected. 5144 assertEquals(photoFileId1, (long) getStoredLongValue( 5145 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1), 5146 Contacts.PHOTO_FILE_ID)); 5147 5148 // 2. Raw contact 2 retains its new photo. The old one is deleted from the photo store. 5149 assertEquals(replacementPhotoFileId, (long) getStoredLongValue( 5150 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2), 5151 Contacts.PHOTO_FILE_ID)); 5152 assertNull(photoStore.get(photoFileId2)); 5153 5154 // 3. Raw contact 3 should have its photo file reference cleared. 5155 assertNull(getStoredValue( 5156 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3), 5157 Contacts.PHOTO_FILE_ID)); 5158 5159 // 4. The bogus photo that nobody was using should be cleared from the photo store. 5160 assertNull(photoStore.get(bogusPhotoId)); 5161 5162 // 5. The bogus stream item photo should be cleared from the stream item. 5163 assertStoredValues(Uri.withAppendedPath( 5164 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 5165 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 5166 new ContentValues[0]); 5167 } 5168 5169 public void testPhotoStoreCleanupForProfile() { 5170 SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider; 5171 PhotoStore profilePhotoStore = provider.getProfilePhotoStore(); 5172 5173 // Trigger an initial cleanup so another one won't happen while we're running this test. 5174 provider.switchToProfileMode(); 5175 provider.cleanupPhotoStore(); 5176 5177 // Create the profile contact and add a photo. 5178 Account socialAccount = new Account("social", "social"); 5179 ContentValues values = new ContentValues(); 5180 values.put(RawContacts.ACCOUNT_NAME, socialAccount.name); 5181 values.put(RawContacts.ACCOUNT_TYPE, socialAccount.type); 5182 long profileRawContactId = createBasicProfileContact(values); 5183 long profileContactId = queryContactId(profileRawContactId); 5184 long dataId = ContentUris.parseId( 5185 insertPhoto(profileRawContactId, R.drawable.earth_normal)); 5186 long profilePhotoFileId = 5187 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), 5188 Photo.PHOTO_FILE_ID); 5189 5190 // Also add a stream item with a photo. 5191 Uri streamItemUri = 5192 insertStreamItem(profileRawContactId, buildGenericStreamItemValues(), 5193 socialAccount); 5194 long streamItemId = ContentUris.parseId(streamItemUri); 5195 Uri streamItemPhotoUri = insertStreamItemPhoto( 5196 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount); 5197 long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri, 5198 StreamItemPhotos.PHOTO_FILE_ID); 5199 5200 // Remove the stream item photo and the profile photo. 5201 profilePhotoStore.remove(profilePhotoFileId); 5202 profilePhotoStore.remove(streamItemPhotoFileId); 5203 5204 // Manually trigger another cleanup in the provider. 5205 provider.switchToProfileMode(); 5206 provider.cleanupPhotoStore(); 5207 5208 // The following things should have happened. 5209 5210 // The stream item photo should have been removed. 5211 assertStoredValues(Uri.withAppendedPath( 5212 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 5213 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 5214 new ContentValues[0]); 5215 5216 // The profile photo should have been cleared. 5217 assertNull(getStoredValue( 5218 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId), 5219 Contacts.PHOTO_FILE_ID)); 5220 5221 } 5222 5223 public void testOverwritePhotoWithThumbnail() throws IOException { 5224 long rawContactId = createRawContactWithName(); 5225 long contactId = queryContactId(rawContactId); 5226 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5227 5228 // Write a regular-size photo. 5229 long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal)); 5230 Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID); 5231 assertTrue(photoFileId != null && photoFileId > 0); 5232 5233 // Now overwrite the photo with a thumbnail-sized photo. 5234 ContentValues update = new ContentValues(); 5235 update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL)); 5236 mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null); 5237 5238 // Photo file ID should have been nulled out, and the photo URI should be the same as the 5239 // thumbnail URI. 5240 assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID)); 5241 String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI); 5242 String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI); 5243 assertEquals(photoUri, thumbnailUri); 5244 5245 // Retrieving the photo URI should get the thumbnail content. 5246 assertInputStreamContent(loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL), 5247 mResolver.openInputStream(Uri.parse(photoUri))); 5248 } 5249 5250 public void testUpdateRawContactSetStarred() { 5251 long rawContactId1 = createRawContactWithName(); 5252 Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 5253 long rawContactId2 = createRawContactWithName(); 5254 Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); 5255 setAggregationException( 5256 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 5257 5258 long contactId = queryContactId(rawContactId1); 5259 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 5260 assertStoredValue(contactUri, Contacts.STARRED, "0"); 5261 5262 ContentValues values = new ContentValues(); 5263 values.put(RawContacts.STARRED, "1"); 5264 5265 mResolver.update(rawContactUri1, values, null, null); 5266 5267 assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); 5268 assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); 5269 assertStoredValue(contactUri, Contacts.STARRED, "1"); 5270 5271 values.put(RawContacts.STARRED, "0"); 5272 mResolver.update(rawContactUri1, values, null, null); 5273 5274 assertStoredValue(rawContactUri1, RawContacts.STARRED, "0"); 5275 assertStoredValue(rawContactUri2, RawContacts.STARRED, "0"); 5276 assertStoredValue(contactUri, Contacts.STARRED, "0"); 5277 5278 values.put(Contacts.STARRED, "1"); 5279 mResolver.update(contactUri, values, null, null); 5280 5281 assertStoredValue(rawContactUri1, RawContacts.STARRED, "1"); 5282 assertStoredValue(rawContactUri2, RawContacts.STARRED, "1"); 5283 assertStoredValue(contactUri, Contacts.STARRED, "1"); 5284 } 5285 5286 public void testSetAndClearSuperPrimaryEmail() { 5287 long rawContactId1 = createRawContact(new Account("a", "a")); 5288 Uri mailUri11 = insertEmail(rawContactId1, "test1 (at) domain1.com"); 5289 Uri mailUri12 = insertEmail(rawContactId1, "test2 (at) domain1.com"); 5290 5291 long rawContactId2 = createRawContact(new Account("b", "b")); 5292 Uri mailUri21 = insertEmail(rawContactId2, "test1 (at) domain2.com"); 5293 Uri mailUri22 = insertEmail(rawContactId2, "test2 (at) domain2.com"); 5294 5295 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 5296 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 5297 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 5298 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 5299 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5300 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5301 assertStoredValue(mailUri22, Data.IS_PRIMARY, 0); 5302 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0); 5303 5304 // Set super primary on the first pair, primary on the second 5305 { 5306 ContentValues values = new ContentValues(); 5307 values.put(Data.IS_SUPER_PRIMARY, 1); 5308 mResolver.update(mailUri11, values, null, null); 5309 } 5310 { 5311 ContentValues values = new ContentValues(); 5312 values.put(Data.IS_SUPER_PRIMARY, 1); 5313 mResolver.update(mailUri22, values, null, null); 5314 } 5315 5316 assertStoredValue(mailUri11, Data.IS_PRIMARY, 1); 5317 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1); 5318 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 5319 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 5320 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5321 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5322 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 5323 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 5324 5325 // Clear primary on the first pair, make sure second is not affected and super_primary is 5326 // also cleared 5327 { 5328 ContentValues values = new ContentValues(); 5329 values.put(Data.IS_PRIMARY, 0); 5330 mResolver.update(mailUri11, values, null, null); 5331 } 5332 5333 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 5334 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 5335 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 5336 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 5337 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5338 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5339 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 5340 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 5341 5342 // Ensure that we can only clear super_primary, if we specify the correct data row 5343 { 5344 ContentValues values = new ContentValues(); 5345 values.put(Data.IS_SUPER_PRIMARY, 0); 5346 mResolver.update(mailUri21, values, null, null); 5347 } 5348 5349 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5350 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5351 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 5352 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 5353 5354 // Ensure that we can only clear primary, if we specify the correct data row 5355 { 5356 ContentValues values = new ContentValues(); 5357 values.put(Data.IS_PRIMARY, 0); 5358 mResolver.update(mailUri21, values, null, null); 5359 } 5360 5361 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5362 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5363 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 5364 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1); 5365 5366 // Now clear super-primary for real 5367 { 5368 ContentValues values = new ContentValues(); 5369 values.put(Data.IS_SUPER_PRIMARY, 0); 5370 mResolver.update(mailUri22, values, null, null); 5371 } 5372 5373 assertStoredValue(mailUri11, Data.IS_PRIMARY, 0); 5374 assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0); 5375 assertStoredValue(mailUri12, Data.IS_PRIMARY, 0); 5376 assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0); 5377 assertStoredValue(mailUri21, Data.IS_PRIMARY, 0); 5378 assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0); 5379 assertStoredValue(mailUri22, Data.IS_PRIMARY, 1); 5380 assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0); 5381 } 5382 5383 /** 5384 * Common function for the testNewPrimaryIn* functions. Its four configurations 5385 * are each called from its own test 5386 */ 5387 public void testChangingPrimary(boolean inUpdate, boolean withSuperPrimary) { 5388 long rawContactId = createRawContact(new Account("a", "a")); 5389 Uri mailUri1 = insertEmail(rawContactId, "test1 (at) domain1.com", true); 5390 5391 if (withSuperPrimary) { 5392 final ContentValues values = new ContentValues(); 5393 values.put(Data.IS_SUPER_PRIMARY, 1); 5394 mResolver.update(mailUri1, values, null, null); 5395 } 5396 5397 assertStoredValue(mailUri1, Data.IS_PRIMARY, 1); 5398 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 5399 5400 // Insert another item 5401 final Uri mailUri2; 5402 if (inUpdate) { 5403 mailUri2 = insertEmail(rawContactId, "test2 (at) domain1.com"); 5404 5405 assertStoredValue(mailUri1, Data.IS_PRIMARY, 1); 5406 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 5407 assertStoredValue(mailUri2, Data.IS_PRIMARY, 0); 5408 assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, 0); 5409 5410 final ContentValues values = new ContentValues(); 5411 values.put(Data.IS_PRIMARY, 1); 5412 mResolver.update(mailUri2, values, null, null); 5413 } else { 5414 // directly add as default 5415 mailUri2 = insertEmail(rawContactId, "test2 (at) domain1.com", true); 5416 } 5417 5418 // Ensure that primary has been unset on the first 5419 // If withSuperPrimary is set, also ensure that is has been moved to the new item 5420 assertStoredValue(mailUri1, Data.IS_PRIMARY, 0); 5421 assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0); 5422 assertStoredValue(mailUri2, Data.IS_PRIMARY, 1); 5423 assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0); 5424 } 5425 5426 public void testNewPrimaryInInsert() { 5427 testChangingPrimary(false, false); 5428 } 5429 5430 public void testNewPrimaryInInsertWithSuperPrimary() { 5431 testChangingPrimary(false, true); 5432 } 5433 5434 public void testNewPrimaryInUpdate() { 5435 testChangingPrimary(true, false); 5436 } 5437 5438 public void testNewPrimaryInUpdateWithSuperPrimary() { 5439 testChangingPrimary(true, true); 5440 } 5441 5442 public void testContactCounts() { 5443 Uri uri = Contacts.CONTENT_URI.buildUpon() 5444 .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build(); 5445 5446 createRawContact(); 5447 createRawContactWithName("James", "Sullivan"); 5448 createRawContactWithName("The Abominable", "Snowman"); 5449 createRawContactWithName("Mike", "Wazowski"); 5450 createRawContactWithName("randall", "boggs"); 5451 createRawContactWithName("Boo", null); 5452 createRawContactWithName("Mary", null); 5453 createRawContactWithName("Roz", null); 5454 5455 Cursor cursor = mResolver.query(uri, 5456 new String[]{Contacts.DISPLAY_NAME}, 5457 null, null, Contacts.SORT_KEY_PRIMARY + " COLLATE LOCALIZED"); 5458 5459 assertFirstLetterValues(cursor, null, "B", "J", "M", "R", "T"); 5460 assertFirstLetterCounts(cursor, 1, 1, 1, 2, 2, 1); 5461 cursor.close(); 5462 5463 cursor = mResolver.query(uri, 5464 new String[]{Contacts.DISPLAY_NAME}, 5465 null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC"); 5466 5467 assertFirstLetterValues(cursor, "W", "S", "R", "M", "B", null); 5468 assertFirstLetterCounts(cursor, 1, 2, 1, 1, 2, 1); 5469 cursor.close(); 5470 } 5471 5472 private void assertFirstLetterValues(Cursor cursor, String... expected) { 5473 String[] actual = cursor.getExtras() 5474 .getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); 5475 MoreAsserts.assertEquals(expected, actual); 5476 } 5477 5478 private void assertFirstLetterCounts(Cursor cursor, int... expected) { 5479 int[] actual = cursor.getExtras() 5480 .getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); 5481 MoreAsserts.assertEquals(expected, actual); 5482 } 5483 5484 public void testReadBooleanQueryParameter() { 5485 assertBooleanUriParameter("foo:bar", "bool", true, true); 5486 assertBooleanUriParameter("foo:bar", "bool", false, false); 5487 assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false); 5488 assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true); 5489 assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false); 5490 assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true); 5491 assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false); 5492 assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false); 5493 assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true); 5494 assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true); 5495 assertBooleanUriParameter("foo:bar?bool", "bool", true, true); 5496 } 5497 5498 private void assertBooleanUriParameter(String uriString, String parameter, 5499 boolean defaultValue, boolean expectedValue) { 5500 assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter( 5501 Uri.parse(uriString), parameter, defaultValue)); 5502 } 5503 5504 public void testGetQueryParameter() { 5505 assertQueryParameter("foo:bar", "param", null); 5506 assertQueryParameter("foo:bar?param", "param", null); 5507 assertQueryParameter("foo:bar?param=", "param", ""); 5508 assertQueryParameter("foo:bar?param=val", "param", "val"); 5509 assertQueryParameter("foo:bar?param=val&some=some", "param", "val"); 5510 assertQueryParameter("foo:bar?some=some¶m=val", "param", "val"); 5511 assertQueryParameter("foo:bar?some=some¶m=val&else=else", "param", "val"); 5512 assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john (at) doe.com"); 5513 assertQueryParameter("foo:bar?some_param=val", "param", null); 5514 assertQueryParameter("foo:bar?some_param=val1¶m=val2", "param", "val2"); 5515 assertQueryParameter("foo:bar?some_param=val1¶m=", "param", ""); 5516 assertQueryParameter("foo:bar?some_param=val1¶m", "param", null); 5517 assertQueryParameter("foo:bar?some_param=val1&another_param=val2¶m=val3", 5518 "param", "val3"); 5519 assertQueryParameter("foo:bar?some_param=val1¶m=val2&some_param=val3", 5520 "param", "val2"); 5521 assertQueryParameter("foo:bar?param=val1&some_param=val2", "param", "val1"); 5522 assertQueryParameter("foo:bar?p=val1&pp=val2", "p", "val1"); 5523 assertQueryParameter("foo:bar?pp=val1&p=val2", "p", "val2"); 5524 assertQueryParameter("foo:bar?ppp=val1&pp=val2&p=val3", "p", "val3"); 5525 assertQueryParameter("foo:bar?ppp=val&", "p", null); 5526 } 5527 5528 public void testMissingAccountTypeParameter() { 5529 // Try querying for RawContacts only using ACCOUNT_NAME 5530 final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter( 5531 RawContacts.ACCOUNT_NAME, "lolwut").build(); 5532 try { 5533 final Cursor cursor = mResolver.query(queryUri, null, null, null, null); 5534 fail("Able to query with incomplete account query parameters"); 5535 } catch (IllegalArgumentException e) { 5536 // Expected behavior. 5537 } 5538 } 5539 5540 public void testInsertInconsistentAccountType() { 5541 // Try inserting RawContact with inconsistent Accounts 5542 final Account red = new Account("red", "red"); 5543 final Account blue = new Account("blue", "blue"); 5544 5545 final ContentValues values = new ContentValues(); 5546 values.put(RawContacts.ACCOUNT_NAME, red.name); 5547 values.put(RawContacts.ACCOUNT_TYPE, red.type); 5548 5549 final Uri insertUri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, blue); 5550 try { 5551 mResolver.insert(insertUri, values); 5552 fail("Able to insert RawContact with inconsistent account details"); 5553 } catch (IllegalArgumentException e) { 5554 // Expected behavior. 5555 } 5556 } 5557 5558 public void testProviderStatusNoContactsNoAccounts() throws Exception { 5559 assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS); 5560 } 5561 5562 public void testProviderStatusOnlyLocalContacts() throws Exception { 5563 long rawContactId = createRawContact(); 5564 assertProviderStatus(ProviderStatus.STATUS_NORMAL); 5565 mResolver.delete( 5566 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), null, null); 5567 assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS); 5568 } 5569 5570 public void testProviderStatusWithAccounts() throws Exception { 5571 assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS); 5572 mActor.setAccounts(new Account[]{ACCOUNT_1}); 5573 ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[]{ACCOUNT_1}); 5574 assertProviderStatus(ProviderStatus.STATUS_NORMAL); 5575 mActor.setAccounts(new Account[0]); 5576 ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[0]); 5577 assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS); 5578 } 5579 5580 private void assertProviderStatus(int expectedProviderStatus) { 5581 Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI, 5582 new String[]{ProviderStatus.DATA1, ProviderStatus.STATUS}, null, null, null); 5583 assertTrue(cursor.moveToFirst()); 5584 assertEquals(0, cursor.getLong(0)); 5585 assertEquals(expectedProviderStatus, cursor.getInt(1)); 5586 cursor.close(); 5587 } 5588 5589 public void testProperties() throws Exception { 5590 ContactsProvider2 provider = (ContactsProvider2)getProvider(); 5591 ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper(); 5592 assertNull(helper.getProperty("non-existent", null)); 5593 assertEquals("default", helper.getProperty("non-existent", "default")); 5594 5595 helper.setProperty("existent1", "string1"); 5596 helper.setProperty("existent2", "string2"); 5597 assertEquals("string1", helper.getProperty("existent1", "default")); 5598 assertEquals("string2", helper.getProperty("existent2", "default")); 5599 helper.setProperty("existent1", null); 5600 assertEquals("default", helper.getProperty("existent1", "default")); 5601 } 5602 5603 private class VCardTestUriCreator { 5604 private String mLookup1; 5605 private String mLookup2; 5606 5607 public VCardTestUriCreator(String lookup1, String lookup2) { 5608 super(); 5609 mLookup1 = lookup1; 5610 mLookup2 = lookup2; 5611 } 5612 5613 public Uri getUri1() { 5614 return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); 5615 } 5616 5617 public Uri getUri2() { 5618 return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); 5619 } 5620 5621 public Uri getCombinedUri() { 5622 return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, 5623 Uri.encode(mLookup1 + ":" + mLookup2)); 5624 } 5625 } 5626 5627 private VCardTestUriCreator createVCardTestContacts() { 5628 final long rawContactId1 = createRawContact(mAccount, RawContacts.SOURCE_ID, "4:12"); 5629 insertStructuredName(rawContactId1, "John", "Doe"); 5630 5631 final long rawContactId2 = createRawContact(mAccount, RawContacts.SOURCE_ID, "3:4%121"); 5632 insertStructuredName(rawContactId2, "Jane", "Doh"); 5633 5634 final long contactId1 = queryContactId(rawContactId1); 5635 final long contactId2 = queryContactId(rawContactId2); 5636 final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); 5637 final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); 5638 final String lookup1 = 5639 Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); 5640 final String lookup2 = 5641 Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); 5642 return new VCardTestUriCreator(lookup1, lookup2); 5643 } 5644 5645 public void testQueryMultiVCard() { 5646 // No need to create any contacts here, because the query for multiple vcards 5647 // does not go into the database at all 5648 Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456")); 5649 Cursor cursor = mResolver.query(uri, null, null, null, null); 5650 assertEquals(1, cursor.getCount()); 5651 assertTrue(cursor.moveToFirst()); 5652 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 5653 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 5654 5655 // The resulting name contains date and time. Ensure that before and after are correct 5656 assertTrue(filename.startsWith("vcards_")); 5657 assertTrue(filename.endsWith(".vcf")); 5658 cursor.close(); 5659 } 5660 5661 public void testQueryFileSingleVCard() { 5662 final VCardTestUriCreator contacts = createVCardTestContacts(); 5663 5664 { 5665 Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null); 5666 assertEquals(1, cursor.getCount()); 5667 assertTrue(cursor.moveToFirst()); 5668 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 5669 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 5670 assertEquals("John Doe.vcf", filename); 5671 cursor.close(); 5672 } 5673 5674 { 5675 Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null); 5676 assertEquals(1, cursor.getCount()); 5677 assertTrue(cursor.moveToFirst()); 5678 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 5679 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 5680 assertEquals("Jane Doh.vcf", filename); 5681 cursor.close(); 5682 } 5683 } 5684 5685 public void testQueryFileProfileVCard() { 5686 createBasicProfileContact(new ContentValues()); 5687 Cursor cursor = mResolver.query(Profile.CONTENT_VCARD_URI, null, null, null, null); 5688 assertEquals(1, cursor.getCount()); 5689 assertTrue(cursor.moveToFirst()); 5690 assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE))); 5691 String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); 5692 assertEquals("Mia Prophyl.vcf", filename); 5693 cursor.close(); 5694 } 5695 5696 public void testOpenAssetFileMultiVCard() throws IOException { 5697 final VCardTestUriCreator contacts = createVCardTestContacts(); 5698 5699 final AssetFileDescriptor descriptor = 5700 mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r"); 5701 final FileInputStream inputStream = descriptor.createInputStream(); 5702 String data = readToEnd(inputStream); 5703 inputStream.close(); 5704 descriptor.close(); 5705 5706 // Ensure that the resulting VCard has both contacts 5707 assertTrue(data.contains("N:Doe;John;;;")); 5708 assertTrue(data.contains("N:Doh;Jane;;;")); 5709 } 5710 5711 public void testOpenAssetFileSingleVCard() throws IOException { 5712 final VCardTestUriCreator contacts = createVCardTestContacts(); 5713 5714 // Ensure that the right VCard is being created in each case 5715 { 5716 final AssetFileDescriptor descriptor = 5717 mResolver.openAssetFileDescriptor(contacts.getUri1(), "r"); 5718 final FileInputStream inputStream = descriptor.createInputStream(); 5719 final String data = readToEnd(inputStream); 5720 inputStream.close(); 5721 descriptor.close(); 5722 5723 assertTrue(data.contains("N:Doe;John;;;")); 5724 assertFalse(data.contains("N:Doh;Jane;;;")); 5725 } 5726 5727 { 5728 final AssetFileDescriptor descriptor = 5729 mResolver.openAssetFileDescriptor(contacts.getUri2(), "r"); 5730 final FileInputStream inputStream = descriptor.createInputStream(); 5731 final String data = readToEnd(inputStream); 5732 inputStream.close(); 5733 descriptor.close(); 5734 5735 assertFalse(data.contains("N:Doe;John;;;")); 5736 assertTrue(data.contains("N:Doh;Jane;;;")); 5737 } 5738 } 5739 5740 public void testAutoGroupMembership() { 5741 long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */); 5742 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 5743 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */); 5744 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */); 5745 long r1 = createRawContact(mAccount); 5746 long r2 = createRawContact(mAccountTwo); 5747 long r3 = createRawContact(null); 5748 5749 Cursor c = queryGroupMemberships(mAccount); 5750 try { 5751 assertTrue(c.moveToNext()); 5752 assertEquals(g1, c.getLong(0)); 5753 assertEquals(r1, c.getLong(1)); 5754 assertFalse(c.moveToNext()); 5755 } finally { 5756 c.close(); 5757 } 5758 5759 c = queryGroupMemberships(mAccountTwo); 5760 try { 5761 assertTrue(c.moveToNext()); 5762 assertEquals(g3, c.getLong(0)); 5763 assertEquals(r2, c.getLong(1)); 5764 assertFalse(c.moveToNext()); 5765 } finally { 5766 c.close(); 5767 } 5768 } 5769 5770 public void testNoAutoAddMembershipAfterGroupCreation() { 5771 long r1 = createRawContact(mAccount); 5772 long r2 = createRawContact(mAccount); 5773 long r3 = createRawContact(mAccount); 5774 long r4 = createRawContact(mAccountTwo); 5775 long r5 = createRawContact(mAccountTwo); 5776 long r6 = createRawContact(null); 5777 5778 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5779 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5780 5781 long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */); 5782 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 5783 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */); 5784 5785 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5786 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5787 } 5788 5789 // create some starred and non-starred contacts, some associated with account, some not 5790 // favorites group created 5791 // the starred contacts should be added to group 5792 // favorites group removed 5793 // no change to starred status 5794 public void testFavoritesMembershipAfterGroupCreation() { 5795 long r1 = createRawContact(mAccount, RawContacts.STARRED, "1"); 5796 long r2 = createRawContact(mAccount); 5797 long r3 = createRawContact(mAccount, RawContacts.STARRED, "1"); 5798 long r4 = createRawContact(mAccountTwo, RawContacts.STARRED, "1"); 5799 long r5 = createRawContact(mAccountTwo); 5800 long r6 = createRawContact(null, RawContacts.STARRED, "1"); 5801 long r7 = createRawContact(null); 5802 5803 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5804 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5805 5806 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 5807 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */); 5808 long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */); 5809 5810 assertTrue(queryRawContactIsStarred(r1)); 5811 assertFalse(queryRawContactIsStarred(r2)); 5812 assertTrue(queryRawContactIsStarred(r3)); 5813 assertTrue(queryRawContactIsStarred(r4)); 5814 assertFalse(queryRawContactIsStarred(r5)); 5815 assertTrue(queryRawContactIsStarred(r6)); 5816 assertFalse(queryRawContactIsStarred(r7)); 5817 5818 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5819 Cursor c = queryGroupMemberships(mAccount); 5820 try { 5821 assertTrue(c.moveToNext()); 5822 assertEquals(g1, c.getLong(0)); 5823 assertEquals(r1, c.getLong(1)); 5824 assertTrue(c.moveToNext()); 5825 assertEquals(g1, c.getLong(0)); 5826 assertEquals(r3, c.getLong(1)); 5827 assertFalse(c.moveToNext()); 5828 } finally { 5829 c.close(); 5830 } 5831 5832 updateItem(RawContacts.CONTENT_URI, r6, 5833 RawContacts.ACCOUNT_NAME, mAccount.name, 5834 RawContacts.ACCOUNT_TYPE, mAccount.type); 5835 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5836 c = queryGroupMemberships(mAccount); 5837 try { 5838 assertTrue(c.moveToNext()); 5839 assertEquals(g1, c.getLong(0)); 5840 assertEquals(r1, c.getLong(1)); 5841 assertTrue(c.moveToNext()); 5842 assertEquals(g1, c.getLong(0)); 5843 assertEquals(r3, c.getLong(1)); 5844 assertTrue(c.moveToNext()); 5845 assertEquals(g1, c.getLong(0)); 5846 assertEquals(r6, c.getLong(1)); 5847 assertFalse(c.moveToNext()); 5848 } finally { 5849 c.close(); 5850 } 5851 5852 mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null); 5853 5854 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5855 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5856 5857 assertTrue(queryRawContactIsStarred(r1)); 5858 assertFalse(queryRawContactIsStarred(r2)); 5859 assertTrue(queryRawContactIsStarred(r3)); 5860 assertTrue(queryRawContactIsStarred(r4)); 5861 assertFalse(queryRawContactIsStarred(r5)); 5862 assertTrue(queryRawContactIsStarred(r6)); 5863 assertFalse(queryRawContactIsStarred(r7)); 5864 } 5865 5866 public void testFavoritesGroupMembershipChangeAfterStarChange() { 5867 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 5868 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */); 5869 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */); 5870 long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */); 5871 long r1 = createRawContact(mAccount, RawContacts.STARRED, "1"); 5872 long r2 = createRawContact(mAccount); 5873 long r3 = createRawContact(mAccountTwo); 5874 5875 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5876 Cursor c = queryGroupMemberships(mAccount); 5877 try { 5878 assertTrue(c.moveToNext()); 5879 assertEquals(g1, c.getLong(0)); 5880 assertEquals(r1, c.getLong(1)); 5881 assertFalse(c.moveToNext()); 5882 } finally { 5883 c.close(); 5884 } 5885 5886 // remove the star from r1 5887 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0")); 5888 5889 // Since no raw contacts are starred, there should be no group memberships. 5890 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5891 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5892 5893 // mark r1 as starred 5894 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1")); 5895 // Now that r1 is starred it should have a membership in the one groups from mAccount 5896 // that is marked as a favorite. 5897 // There should be no memberships in mAccountTwo since it has no starred raw contacts. 5898 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5899 c = queryGroupMemberships(mAccount); 5900 try { 5901 assertTrue(c.moveToNext()); 5902 assertEquals(g1, c.getLong(0)); 5903 assertEquals(r1, c.getLong(1)); 5904 assertFalse(c.moveToNext()); 5905 } finally { 5906 c.close(); 5907 } 5908 5909 // remove the star from r1 5910 assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0")); 5911 // Since no raw contacts are starred, there should be no group memberships. 5912 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5913 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5914 5915 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1)); 5916 assertNotNull(contactUri); 5917 5918 // mark r1 as starred via its contact lookup uri 5919 assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1")); 5920 // Now that r1 is starred it should have a membership in the one groups from mAccount 5921 // that is marked as a favorite. 5922 // There should be no memberships in mAccountTwo since it has no starred raw contacts. 5923 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5924 c = queryGroupMemberships(mAccount); 5925 try { 5926 assertTrue(c.moveToNext()); 5927 assertEquals(g1, c.getLong(0)); 5928 assertEquals(r1, c.getLong(1)); 5929 assertFalse(c.moveToNext()); 5930 } finally { 5931 c.close(); 5932 } 5933 5934 // remove the star from r1 5935 updateItem(contactUri, Contacts.STARRED, "0"); 5936 // Since no raw contacts are starred, there should be no group memberships. 5937 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5938 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5939 } 5940 5941 public void testStarChangedAfterGroupMembershipChange() { 5942 long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */); 5943 long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */); 5944 long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */); 5945 long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */); 5946 long r1 = createRawContact(mAccount); 5947 long r2 = createRawContact(mAccount); 5948 long r3 = createRawContact(mAccountTwo); 5949 5950 assertFalse(queryRawContactIsStarred(r1)); 5951 assertFalse(queryRawContactIsStarred(r2)); 5952 assertFalse(queryRawContactIsStarred(r3)); 5953 5954 Cursor c; 5955 5956 // add r1 to one favorites group 5957 // r1's star should automatically be set 5958 // r1 should automatically be added to the other favorites group 5959 Uri urir1g1 = insertGroupMembership(r1, g1); 5960 assertTrue(queryRawContactIsStarred(r1)); 5961 assertFalse(queryRawContactIsStarred(r2)); 5962 assertFalse(queryRawContactIsStarred(r3)); 5963 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5964 c = queryGroupMemberships(mAccount); 5965 try { 5966 assertTrue(c.moveToNext()); 5967 assertEquals(g1, c.getLong(0)); 5968 assertEquals(r1, c.getLong(1)); 5969 assertFalse(c.moveToNext()); 5970 } finally { 5971 c.close(); 5972 } 5973 5974 // remove r1 from one favorites group 5975 mResolver.delete(urir1g1, null, null); 5976 // r1's star should no longer be set 5977 assertFalse(queryRawContactIsStarred(r1)); 5978 assertFalse(queryRawContactIsStarred(r2)); 5979 assertFalse(queryRawContactIsStarred(r3)); 5980 // there should be no membership rows 5981 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5982 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 5983 5984 // add r3 to the one favorites group for that account 5985 // r3's star should automatically be set 5986 Uri urir3g4 = insertGroupMembership(r3, g4); 5987 assertFalse(queryRawContactIsStarred(r1)); 5988 assertFalse(queryRawContactIsStarred(r2)); 5989 assertTrue(queryRawContactIsStarred(r3)); 5990 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 5991 c = queryGroupMemberships(mAccountTwo); 5992 try { 5993 assertTrue(c.moveToNext()); 5994 assertEquals(g4, c.getLong(0)); 5995 assertEquals(r3, c.getLong(1)); 5996 assertFalse(c.moveToNext()); 5997 } finally { 5998 c.close(); 5999 } 6000 6001 // remove r3 from the favorites group 6002 mResolver.delete(urir3g4, null, null); 6003 // r3's star should automatically be cleared 6004 assertFalse(queryRawContactIsStarred(r1)); 6005 assertFalse(queryRawContactIsStarred(r2)); 6006 assertFalse(queryRawContactIsStarred(r3)); 6007 assertNoRowsAndClose(queryGroupMemberships(mAccount)); 6008 assertNoRowsAndClose(queryGroupMemberships(mAccountTwo)); 6009 } 6010 6011 public void testReadOnlyRawContact() { 6012 long rawContactId = createRawContact(); 6013 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 6014 storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first"); 6015 storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1); 6016 6017 storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second"); 6018 assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first"); 6019 6020 Uri syncAdapterUri = rawContactUri.buildUpon() 6021 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1") 6022 .build(); 6023 storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third"); 6024 assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third"); 6025 } 6026 6027 public void testReadOnlyDataRow() { 6028 long rawContactId = createRawContact(); 6029 Uri emailUri = insertEmail(rawContactId, "email"); 6030 Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111"); 6031 6032 storeValue(emailUri, Data.IS_READ_ONLY, "1"); 6033 storeValue(emailUri, Email.ADDRESS, "changed"); 6034 storeValue(phoneUri, Phone.NUMBER, "555-2222"); 6035 assertStoredValue(emailUri, Email.ADDRESS, "email"); 6036 assertStoredValue(phoneUri, Phone.NUMBER, "555-2222"); 6037 6038 Uri syncAdapterUri = emailUri.buildUpon() 6039 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1") 6040 .build(); 6041 storeValue(syncAdapterUri, Email.ADDRESS, "changed"); 6042 assertStoredValue(emailUri, Email.ADDRESS, "changed"); 6043 } 6044 6045 public void testContactWithReadOnlyRawContact() { 6046 long rawContactId1 = createRawContact(); 6047 Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1); 6048 storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first"); 6049 6050 long rawContactId2 = createRawContact(); 6051 Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2); 6052 storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second"); 6053 storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1); 6054 6055 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 6056 rawContactId1, rawContactId2); 6057 6058 long contactId = queryContactId(rawContactId1); 6059 6060 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 6061 storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt"); 6062 assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt"); 6063 assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt"); 6064 assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second"); 6065 } 6066 6067 public void testNameParsingQuery() { 6068 Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name") 6069 .appendQueryParameter(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.").build(); 6070 Cursor cursor = mResolver.query(uri, null, null, null, null); 6071 ContentValues values = new ContentValues(); 6072 values.put(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr."); 6073 values.put(StructuredName.PREFIX, "Mr."); 6074 values.put(StructuredName.GIVEN_NAME, "John"); 6075 values.put(StructuredName.MIDDLE_NAME, "Q."); 6076 values.put(StructuredName.FAMILY_NAME, "Doe"); 6077 values.put(StructuredName.SUFFIX, "Jr."); 6078 values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN); 6079 assertTrue(cursor.moveToFirst()); 6080 assertCursorValues(cursor, values); 6081 cursor.close(); 6082 } 6083 6084 public void testNameConcatenationQuery() { 6085 Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name") 6086 .appendQueryParameter(StructuredName.PREFIX, "Mr") 6087 .appendQueryParameter(StructuredName.GIVEN_NAME, "John") 6088 .appendQueryParameter(StructuredName.MIDDLE_NAME, "Q.") 6089 .appendQueryParameter(StructuredName.FAMILY_NAME, "Doe") 6090 .appendQueryParameter(StructuredName.SUFFIX, "Jr.") 6091 .build(); 6092 Cursor cursor = mResolver.query(uri, null, null, null, null); 6093 ContentValues values = new ContentValues(); 6094 values.put(StructuredName.DISPLAY_NAME, "Mr John Q. Doe, Jr."); 6095 values.put(StructuredName.PREFIX, "Mr"); 6096 values.put(StructuredName.GIVEN_NAME, "John"); 6097 values.put(StructuredName.MIDDLE_NAME, "Q."); 6098 values.put(StructuredName.FAMILY_NAME, "Doe"); 6099 values.put(StructuredName.SUFFIX, "Jr."); 6100 values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN); 6101 assertTrue(cursor.moveToFirst()); 6102 assertCursorValues(cursor, values); 6103 cursor.close(); 6104 } 6105 6106 private Cursor queryGroupMemberships(Account account) { 6107 Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account), 6108 new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID}, 6109 Data.MIMETYPE + "=?", new String[]{GroupMembership.CONTENT_ITEM_TYPE}, 6110 GroupMembership.GROUP_SOURCE_ID); 6111 return c; 6112 } 6113 6114 private String readToEnd(FileInputStream inputStream) { 6115 try { 6116 System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); 6117 int ch; 6118 StringBuilder stringBuilder = new StringBuilder(); 6119 int index = 0; 6120 while (true) { 6121 ch = inputStream.read(); 6122 System.out.println("READ CHARACTER: " + index + " " + ch); 6123 if (ch == -1) { 6124 break; 6125 } 6126 stringBuilder.append((char)ch); 6127 index++; 6128 } 6129 return stringBuilder.toString(); 6130 } catch (IOException e) { 6131 return null; 6132 } 6133 } 6134 6135 private void assertQueryParameter(String uriString, String parameter, String expectedValue) { 6136 assertEquals(expectedValue, ContactsProvider2.getQueryParameter( 6137 Uri.parse(uriString), parameter)); 6138 } 6139 6140 private long createContact(ContentValues values, String firstName, String givenName, 6141 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 6142 long groupId, int chatMode) { 6143 return createContact(values, firstName, givenName, phoneNumber, email, presenceStatus, 6144 timesContacted, starred, groupId, chatMode, false); 6145 } 6146 6147 private long createContact(ContentValues values, String firstName, String givenName, 6148 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 6149 long groupId, int chatMode, boolean isUserProfile) { 6150 return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email, 6151 presenceStatus, timesContacted, starred, groupId, chatMode, isUserProfile)); 6152 } 6153 6154 private long createRawContact(ContentValues values, String firstName, String givenName, 6155 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 6156 long groupId, int chatMode) { 6157 long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus, 6158 timesContacted, starred, groupId, chatMode); 6159 insertStructuredName(rawContactId, firstName, givenName); 6160 return rawContactId; 6161 } 6162 6163 private long createRawContact(ContentValues values, String firstName, String givenName, 6164 String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, 6165 long groupId, int chatMode, boolean isUserProfile) { 6166 long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus, 6167 timesContacted, starred, groupId, chatMode, isUserProfile); 6168 insertStructuredName(rawContactId, firstName, givenName); 6169 return rawContactId; 6170 } 6171 6172 private long createRawContact(ContentValues values, String phoneNumber, String email, 6173 int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) { 6174 return createRawContact(values, phoneNumber, email, presenceStatus, timesContacted, starred, 6175 groupId, chatMode, false); 6176 } 6177 6178 private long createRawContact(ContentValues values, String phoneNumber, String email, 6179 int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, 6180 boolean isUserProfile) { 6181 values.put(RawContacts.STARRED, starred); 6182 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 6183 values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5"); 6184 values.put(RawContacts.TIMES_CONTACTED, timesContacted); 6185 6186 Uri insertionUri = isUserProfile 6187 ? Profile.CONTENT_RAW_CONTACTS_URI 6188 : RawContacts.CONTENT_URI; 6189 Uri rawContactUri = mResolver.insert(insertionUri, values); 6190 long rawContactId = ContentUris.parseId(rawContactUri); 6191 Uri photoUri = insertPhoto(rawContactId); 6192 long photoId = ContentUris.parseId(photoUri); 6193 values.put(Contacts.PHOTO_ID, photoId); 6194 if (!TextUtils.isEmpty(phoneNumber)) { 6195 insertPhoneNumber(rawContactId, phoneNumber); 6196 } 6197 if (!TextUtils.isEmpty(email)) { 6198 insertEmail(rawContactId, email); 6199 } 6200 6201 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking", 6202 chatMode, isUserProfile); 6203 6204 if (groupId != 0) { 6205 insertGroupMembership(rawContactId, groupId); 6206 } 6207 6208 return rawContactId; 6209 } 6210 6211 /** 6212 * Creates a raw contact with pre-set values under the user's profile. 6213 * @param profileValues Values to be used to create the entry (common values will be 6214 * automatically populated in createRawContact()). 6215 * @return the raw contact ID that was created. 6216 */ 6217 private long createBasicProfileContact(ContentValues profileValues) { 6218 long profileRawContactId = createRawContact(profileValues, "Mia", "Prophyl", 6219 "18005554411", "mia.prophyl (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 6220 StatusUpdates.CAPABILITY_HAS_CAMERA, true); 6221 profileValues.put(Contacts.DISPLAY_NAME, "Mia Prophyl"); 6222 return profileRawContactId; 6223 } 6224 6225 /** 6226 * Creates a raw contact with pre-set values that is not under the user's profile. 6227 * @param nonProfileValues Values to be used to create the entry (common values will be 6228 * automatically populated in createRawContact()). 6229 * @return the raw contact ID that was created. 6230 */ 6231 private long createBasicNonProfileContact(ContentValues nonProfileValues) { 6232 long nonProfileRawContactId = createRawContact(nonProfileValues, "John", "Doe", 6233 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 6234 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 6235 nonProfileValues.put(Contacts.DISPLAY_NAME, "John Doe"); 6236 return nonProfileRawContactId; 6237 } 6238 6239 private void putDataValues(ContentValues values, long rawContactId) { 6240 values.put(Data.RAW_CONTACT_ID, rawContactId); 6241 values.put(Data.MIMETYPE, "testmimetype"); 6242 values.put(Data.RES_PACKAGE, "oldpackage"); 6243 values.put(Data.IS_PRIMARY, 1); 6244 values.put(Data.IS_SUPER_PRIMARY, 1); 6245 values.put(Data.DATA1, "one"); 6246 values.put(Data.DATA2, "two"); 6247 values.put(Data.DATA3, "three"); 6248 values.put(Data.DATA4, "four"); 6249 values.put(Data.DATA5, "five"); 6250 values.put(Data.DATA6, "six"); 6251 values.put(Data.DATA7, "seven"); 6252 values.put(Data.DATA8, "eight"); 6253 values.put(Data.DATA9, "nine"); 6254 values.put(Data.DATA10, "ten"); 6255 values.put(Data.DATA11, "eleven"); 6256 values.put(Data.DATA12, "twelve"); 6257 values.put(Data.DATA13, "thirteen"); 6258 values.put(Data.DATA14, "fourteen"); 6259 values.put(Data.DATA15, "fifteen"); 6260 values.put(Data.SYNC1, "sync1"); 6261 values.put(Data.SYNC2, "sync2"); 6262 values.put(Data.SYNC3, "sync3"); 6263 values.put(Data.SYNC4, "sync4"); 6264 } 6265 6266 /** 6267 * @param data1 email address or phone number 6268 * @param usageType One of {@link DataUsageFeedback#USAGE_TYPE} 6269 * @param values ContentValues for this feedback. Useful for incrementing 6270 * {Contacts#TIMES_CONTACTED} in the ContentValue. Can be null. 6271 */ 6272 private void sendFeedback(String data1, String usageType, ContentValues values) { 6273 final long dataId = getStoredLongValue(Data.CONTENT_URI, 6274 Data.DATA1 + "=?", new String[] { data1 }, Data._ID); 6275 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 6276 .appendPath(String.valueOf(dataId)) 6277 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType) 6278 .build(); 6279 assertNotSame(0, mResolver.update(feedbackUri, new ContentValues(), null, null)); 6280 if (values != null && values.containsKey(Contacts.TIMES_CONTACTED)) { 6281 values.put(Contacts.TIMES_CONTACTED, values.getAsInteger(Contacts.TIMES_CONTACTED) + 1); 6282 } 6283 } 6284 } 6285