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