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