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