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