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