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