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