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