1 /* 2 * Copyright (C) 2011 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.dialer.calllog; 18 19 import com.google.common.collect.Lists; 20 21 import android.content.ContentUris; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.res.Resources; 25 import android.database.MatrixCursor; 26 import android.net.Uri; 27 import android.provider.CallLog.Calls; 28 import android.provider.ContactsContract.CommonDataKinds.Phone; 29 import android.provider.VoicemailContract; 30 import android.telephony.PhoneNumberUtils; 31 import android.test.AndroidTestCase; 32 import android.test.suitebuilder.annotation.MediumTest; 33 import android.text.TextUtils; 34 import android.view.View; 35 36 import com.android.contacts.common.compat.CompatUtils; 37 import com.android.contacts.common.preference.ContactsPreferences; 38 import com.android.dialer.contactinfo.ContactInfoCache; 39 import com.android.dialer.database.VoicemailArchiveContract; 40 import com.android.dialer.util.AppCompatConstants; 41 import com.android.dialer.util.TestConstants; 42 import com.android.dialer.R; 43 44 import java.util.Date; 45 import java.util.List; 46 import java.util.Random; 47 48 /** 49 * Unit tests for {@link CallLogAdapter}. 50 * 51 * adb shell am instrument \ 52 * -e com.android.dialer.calllog.CallLogAdapterTest \ 53 * -w com.android.dialer.tests/android.test.InstrumentationTestRunner 54 */ 55 public class CallLogAdapterTest extends AndroidTestCase { 56 private static final String EMPTY_STRING = ""; 57 private static final int NO_VALUE_SET = -1; 58 private static final int ARCHIVE_TYPE = -2; 59 60 private static final String TEST_CACHED_NAME_PRIMARY = "Cached Name"; 61 private static final String TEST_CACHED_NAME_ALTERNATIVE = "Name Cached"; 62 private static final String CONTACT_NAME_PRIMARY = "Contact Name"; 63 private static final String CONTACT_NAME_ALTERNATIVE = "Name, Contact"; 64 private static final String TEST_CACHED_NUMBER_LABEL = "label"; 65 private static final int TEST_CACHED_NUMBER_TYPE = 1; 66 private static final String TEST_COUNTRY_ISO = "US"; 67 private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel"; 68 private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2"); 69 private static final String TEST_ACCOUNT_ID_LABEL = "label"; 70 71 private static final String TEST_NUMBER = "12125551000"; 72 private static final String TEST_NUMBER_1 = "12345678"; 73 private static final String TEST_NUMBER_2 = "87654321"; 74 private static final String TEST_NUMBER_3 = "18273645"; 75 private static final String TEST_POST_DIAL_DIGITS = ";12345"; 76 private static final String TEST_VIA_NUMBER = "+16505551234"; 77 private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000"; 78 79 // The object under test. 80 private TestCallLogAdapter mAdapter; 81 82 private MatrixCursor mCursor; 83 private Resources mResources; 84 85 private CallLogListItemViewHolder mViewHolder; 86 private final Random mRandom = new Random(); 87 88 @Override setUp()89 protected void setUp() throws Exception { 90 super.setUp(); 91 mContext = getContext(); 92 mResources = mContext.getResources(); 93 94 // Use a call fetcher that does not do anything. 95 CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() { 96 @Override 97 public void fetchCalls() {} 98 }; 99 100 ContactInfoHelper fakeContactInfoHelper = 101 new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) { 102 @Override 103 public ContactInfo lookupNumber(String number, String countryIso) { 104 ContactInfo info = new ContactInfo(); 105 info.number = number; 106 info.formattedNumber = number; 107 return info; 108 } 109 }; 110 111 mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper, 112 CallLogAdapter.ACTIVITY_TYPE_DIALTACTS); 113 114 // The cursor used in the tests to store the entries to display. 115 mCursor = new MatrixCursor(CallLogQuery._PROJECTION); 116 mCursor.moveToFirst(); 117 118 // The views into which to store the data. 119 mViewHolder = CallLogListItemViewHolder.createForTest(getContext()); 120 } 121 122 @MediumTest testBindView_NumberOnlyNoCache()123 public void testBindView_NumberOnlyNoCache() { 124 createCallLogEntry(); 125 126 mAdapter.changeCursor(mCursor); 127 mAdapter.onBindViewHolder(mViewHolder, 0); 128 129 assertNameIs(mViewHolder, TEST_NUMBER); 130 } 131 132 @MediumTest testBindView_PrivateCall()133 public void testBindView_PrivateCall() { 134 createPrivateCallLogEntry(); 135 136 mAdapter.changeCursor(mCursor); 137 mAdapter.onBindViewHolder(mViewHolder, 0); 138 139 assertEquals(Calls.PRESENTATION_RESTRICTED, mViewHolder.numberPresentation); 140 assertNull(mViewHolder.primaryActionButtonView.getTag()); 141 // QC should be disabled since there are no actions to be performed on this 142 // call. 143 assertFalse(mViewHolder.quickContactView.isEnabled()); 144 } 145 146 @MediumTest testBindView_UnknownCall()147 public void testBindView_UnknownCall() { 148 createUnknownCallLogEntry(); 149 150 mAdapter.changeCursor(mCursor); 151 mAdapter.onBindViewHolder(mViewHolder, 0); 152 153 assertEquals(Calls.PRESENTATION_UNKNOWN, mViewHolder.numberPresentation); 154 assertNull(mViewHolder.primaryActionButtonView.getTag()); 155 // QC should be disabled since there are no actions to be performed on this 156 // call. 157 assertFalse(mViewHolder.quickContactView.isEnabled()); 158 } 159 160 @MediumTest testBindView_WithoutQuickContactBadge()161 public void testBindView_WithoutQuickContactBadge() { 162 createCallLogEntry(); 163 164 mAdapter.changeCursor(mCursor); 165 mAdapter.onBindViewHolder(mViewHolder, 0); 166 167 //assertFalse(mViewHolder.quickContactView.isEnabled()); 168 } 169 170 @MediumTest testBindView_CallButton()171 public void testBindView_CallButton() { 172 createCallLogEntry(); 173 174 mAdapter.changeCursor(mCursor); 175 mAdapter.onBindViewHolder(mViewHolder, 0); 176 177 // The primaryActionView tag is set when the ViewHolder is binded. If it is possible 178 // to place a call to the phone number, a call intent will have been created which 179 // starts a phone call to the entry's number. 180 assertHasCallAction(mViewHolder); 181 } 182 183 @MediumTest testBindView_FirstNameFirstOrder()184 public void testBindView_FirstNameFirstOrder() { 185 createCallLogEntry(); 186 187 mAdapter.getContactInfoCache() 188 .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE)); 189 190 setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_PRIMARY); 191 192 mAdapter.changeCursor(mCursor); 193 mAdapter.onBindViewHolder(mViewHolder, 0); 194 assertEquals(CONTACT_NAME_PRIMARY, mViewHolder.phoneCallDetailsViews.nameView.getText()); 195 } 196 197 @MediumTest testBindView_LastNameFirstOrder()198 public void testBindView_LastNameFirstOrder() { 199 createCallLogEntry(); 200 201 mAdapter.getContactInfoCache() 202 .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE)); 203 204 setNameDisplayOrder(getContext(), ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE); 205 206 mAdapter.changeCursor(mCursor); 207 mAdapter.onBindViewHolder(mViewHolder, 0); 208 assertEquals(CONTACT_NAME_ALTERNATIVE, 209 mViewHolder.phoneCallDetailsViews.nameView.getText()); 210 } 211 212 @MediumTest testBindView_NameOrderCorrectOnChange()213 public void testBindView_NameOrderCorrectOnChange() { 214 createCallLogEntry(); 215 216 mAdapter.getContactInfoCache() 217 .mockGetValue(createContactInfo(CONTACT_NAME_PRIMARY, CONTACT_NAME_ALTERNATIVE)); 218 219 Context context = getContext(); 220 setNameDisplayOrder(context, ContactsPreferences.DISPLAY_ORDER_PRIMARY); 221 222 mAdapter.changeCursor(mCursor); 223 mAdapter.onBindViewHolder(mViewHolder, 0); 224 assertEquals(CONTACT_NAME_PRIMARY, 225 mViewHolder.phoneCallDetailsViews.nameView.getText()); 226 227 setNameDisplayOrder(context, ContactsPreferences.DISPLAY_ORDER_ALTERNATIVE); 228 mAdapter.onResume(); 229 230 mAdapter.onBindViewHolder(mViewHolder, 0); 231 assertEquals(CONTACT_NAME_ALTERNATIVE, 232 mViewHolder.phoneCallDetailsViews.nameView.getText()); 233 } 234 setNameDisplayOrder(Context context, int displayOrder)235 private void setNameDisplayOrder(Context context, int displayOrder) { 236 context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit().putInt( 237 ContactsPreferences.DISPLAY_ORDER_KEY, displayOrder).commit(); 238 } 239 240 @MediumTest testBindView_CallButtonWithPostDialDigits()241 public void testBindView_CallButtonWithPostDialDigits() { 242 createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET); 243 244 mAdapter.changeCursor(mCursor); 245 mAdapter.onBindViewHolder(mViewHolder, 0); 246 247 if (CompatUtils.isNCompatible()) { 248 assertHasCallActionToGivenNumber(mViewHolder, TEST_NUMBER + TEST_POST_DIAL_DIGITS); 249 } 250 } 251 252 @MediumTest testBindView_VoicemailUri()253 public void testBindView_VoicemailUri() { 254 createVoicemailCallLogEntry(); 255 256 mAdapter.changeCursor(mCursor); 257 mAdapter.onBindViewHolder(mViewHolder, 0); 258 259 assertEquals(Uri.parse(mViewHolder.voicemailUri), 260 ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, 0)); 261 assertNull(mViewHolder.primaryActionButtonView.getTag()); 262 } 263 264 @MediumTest testBindView_NumberWithPostDialDigits()265 public void testBindView_NumberWithPostDialDigits() { 266 createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET); 267 268 mAdapter.changeCursor(mCursor); 269 mAdapter.onBindViewHolder(mViewHolder, 0); 270 271 if (CompatUtils.isNCompatible()) { 272 assertNameIs(mViewHolder, TEST_NUMBER + TEST_POST_DIAL_DIGITS); 273 } 274 } 275 276 @MediumTest testBindView_ContactWithPostDialDigits()277 public void testBindView_ContactWithPostDialDigits() { 278 createCallLogEntry(TEST_NUMBER, TEST_POST_DIAL_DIGITS, NO_VALUE_SET, NO_VALUE_SET); 279 mAdapter.injectContactInfoForTest(TEST_NUMBER + TEST_POST_DIAL_DIGITS, TEST_COUNTRY_ISO, 280 createContactInfo()); 281 282 mAdapter.changeCursor(mCursor); 283 mAdapter.onBindViewHolder(mViewHolder, 0); 284 285 if (CompatUtils.isNCompatible()) { 286 assertNameIs(mViewHolder, TEST_CACHED_NAME_PRIMARY); 287 } 288 } 289 290 @MediumTest testBindView_CallLogWithViaNumber()291 public void testBindView_CallLogWithViaNumber() { 292 createCallLogEntry(TEST_NUMBER, EMPTY_STRING, TEST_VIA_NUMBER, NO_VALUE_SET, NO_VALUE_SET); 293 294 mAdapter.changeCursor(mCursor); 295 mAdapter.onBindViewHolder(mViewHolder, 0); 296 297 // Copy format of Resource String 298 String formattedNumber = mResources.getString(R.string.description_via_number, 299 TEST_VIA_NUMBER); 300 301 if (CompatUtils.isNCompatible()) { 302 assertEquals(formattedNumber, 303 mViewHolder.phoneCallDetailsViews.callAccountLabel.getText()); 304 } 305 } 306 307 @MediumTest testBindView_CallLogWithoutViaNumber()308 public void testBindView_CallLogWithoutViaNumber() { 309 createCallLogEntry(TEST_NUMBER, EMPTY_STRING, EMPTY_STRING, NO_VALUE_SET, NO_VALUE_SET); 310 311 mAdapter.changeCursor(mCursor); 312 mAdapter.onBindViewHolder(mViewHolder, 0); 313 314 if (CompatUtils.isNCompatible()) { 315 assertEquals(View.GONE, 316 mViewHolder.phoneCallDetailsViews.callAccountLabel.getVisibility()); 317 } 318 } 319 320 @MediumTest testPresentationAfterRebindingViewHolders()321 public void testPresentationAfterRebindingViewHolders() { 322 final int increment = 10; 323 final int size = increment * 4; 324 325 // Instantiate list of ViewHolders. 326 CallLogListItemViewHolder[] holders = new CallLogListItemViewHolder[size]; 327 for (int i = 0; i < size; i++) { 328 holders[i] = CallLogListItemViewHolder.createForTest(getContext()); 329 } 330 331 // Add first set of entries to the cursor. 332 for (int i = 0; i < increment; i++) { 333 createCallLogEntry(); 334 createPrivateCallLogEntry(); 335 createCallLogEntry(); 336 createUnknownCallLogEntry(); 337 } 338 339 mAdapter.changeCursor(mCursor); 340 341 // Verify correct appearance for presentation. 342 for (int i = 0; i < size; i++) { 343 mAdapter.onBindViewHolder(holders[i], i); 344 if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) { 345 assertHasCallAction(holders[i]); 346 } else { 347 assertNull(holders[i].primaryActionButtonView.getTag()); 348 assertEquals(holders[i].number, EMPTY_STRING); 349 } 350 } 351 352 // Append the rest of the entries to the cursor. Keep the first set of ViewHolders 353 // so they are updated and not buitl from scratch. This checks for bugs which may 354 // be evident only after the call log is updated. 355 for (int i = 0; i < increment; i++) { 356 createPrivateCallLogEntry(); 357 createCallLogEntry(); 358 createUnknownCallLogEntry(); 359 createCallLogEntry(); 360 } 361 362 mCursor.move(size); 363 364 // Verify correct appearnce for presentation. 365 for (int i = 0; i < size; i++) { 366 mAdapter.onBindViewHolder(holders[i], i + size); 367 if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) { 368 assertHasCallAction(holders[i]); 369 } else { 370 assertNull(holders[i].primaryActionButtonView.getTag()); 371 assertEquals(holders[i].number, EMPTY_STRING); 372 } 373 } 374 } 375 376 @MediumTest testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest()377 public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() { 378 createCallLogEntry(); 379 380 // Bind the views of a single row. 381 mAdapter.changeCursor(mCursor); 382 mAdapter.onBindViewHolder(mViewHolder, 0); 383 384 // There is one request for contact details. 385 assertEquals(1, mAdapter.getContactInfoCache().requests.size()); 386 387 TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); 388 // It is for the number we need to show. 389 assertEquals(TEST_NUMBER, request.number); 390 // It has the right country. 391 assertEquals(TEST_COUNTRY_ISO, request.countryIso); 392 // Since there is nothing in the cache, it is an immediate request. 393 assertTrue("should be immediate", request.immediate); 394 } 395 396 @MediumTest testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest()397 public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() { 398 createCallLogEntryWithCachedValues(false); 399 400 // Bind the views of a single row. 401 mAdapter.changeCursor(mCursor); 402 mAdapter.onBindViewHolder(mViewHolder, 0); 403 404 // There is one request for contact details. 405 assertEquals(1, mAdapter.getContactInfoCache().requests.size()); 406 407 TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); 408 409 // The values passed to the request, match the ones in the call log cache. 410 assertEquals(TEST_CACHED_NAME_PRIMARY, request.callLogInfo.name); 411 assertEquals(TEST_CACHED_NUMBER_TYPE, request.callLogInfo.type); 412 assertEquals(TEST_CACHED_NUMBER_LABEL, request.callLogInfo.label); 413 } 414 415 @MediumTest testBindView_NoCallLogButMemoryCache_EnqueueRequest()416 public void testBindView_NoCallLogButMemoryCache_EnqueueRequest() { 417 createCallLogEntry(); 418 mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo()); 419 420 // Bind the views of a single row. 421 mAdapter.changeCursor(mCursor); 422 mAdapter.onBindViewHolder(mViewHolder, 0); 423 424 // There is one request for contact details. 425 assertEquals(1, mAdapter.getContactInfoCache().requests.size()); 426 427 TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); 428 // Since there is something in the cache, it is not an immediate request. 429 assertFalse("should not be immediate", request.immediate); 430 } 431 432 @MediumTest testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest()433 public void testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest() { 434 createCallLogEntryWithCachedValues(true); 435 436 // Bind the views of a single row. 437 mAdapter.changeCursor(mCursor); 438 mAdapter.onBindViewHolder(mViewHolder, 0); 439 440 // Cache and call log are up-to-date: no need to request update. 441 assertEquals(0, mAdapter.getContactInfoCache().requests.size()); 442 } 443 444 @MediumTest testBindView_MismatchBetweenCallLogAndMemoryCache_EnqueueRequest()445 public void testBindView_MismatchBetweenCallLogAndMemoryCache_EnqueueRequest() { 446 createCallLogEntryWithCachedValues(false); 447 448 // Contact info contains a different name. 449 ContactInfo info = createContactInfo(); 450 info.name = "new name"; 451 mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, info); 452 453 // Bind the views of a single row. 454 mAdapter.changeCursor(mCursor); 455 mAdapter.onBindViewHolder(mViewHolder, 0); 456 457 // There is one request for contact details. 458 assertEquals(1, mAdapter.getContactInfoCache().requests.size()); 459 460 TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); 461 // Since there is something in the cache, it is not an immediate request. 462 assertFalse("should not be immediate", request.immediate); 463 } 464 465 @MediumTest testBindView_WithCachedName()466 public void testBindView_WithCachedName() { 467 createCallLogEntryWithCachedValues( 468 "John Doe", 469 Phone.TYPE_HOME, 470 TEST_CACHED_NUMBER_LABEL); 471 472 mAdapter.changeCursor(mCursor); 473 mAdapter.onBindViewHolder(mViewHolder, 0); 474 475 assertNameIs(mViewHolder, "John Doe"); 476 assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); 477 } 478 479 @MediumTest testBindView_UriNumber()480 public void testBindView_UriNumber() { 481 createCallLogEntryWithCachedValues( 482 "sip:johndoe@gmail.com", 483 AppCompatConstants.CALLS_INCOMING_TYPE, 484 "John Doe", 485 Phone.TYPE_HOME, 486 TEST_DEFAULT_CUSTOM_LABEL, 487 EMPTY_STRING, 488 false /* inject */); 489 490 mAdapter.changeCursor(mCursor); 491 mAdapter.onBindViewHolder(mViewHolder, 0); 492 493 assertNameIs(mViewHolder, "John Doe"); 494 assertLabel(mViewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com"); 495 } 496 497 @MediumTest testBindView_HomeLabel()498 public void testBindView_HomeLabel() { 499 createCallLogEntryWithCachedValues( 500 "John Doe", 501 Phone.TYPE_HOME, 502 TEST_CACHED_NUMBER_LABEL); 503 504 mAdapter.changeCursor(mCursor); 505 mAdapter.onBindViewHolder(mViewHolder, 0); 506 507 assertNameIs(mViewHolder, "John Doe"); 508 assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); 509 } 510 511 @MediumTest testBindView_WorkLabel()512 public void testBindView_WorkLabel() { 513 createCallLogEntryWithCachedValues( 514 "John Doe", 515 Phone.TYPE_WORK, 516 TEST_CACHED_NUMBER_LABEL); 517 518 mAdapter.changeCursor(mCursor); 519 mAdapter.onBindViewHolder(mViewHolder, 0); 520 521 assertNameIs(mViewHolder, "John Doe"); 522 assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK)); 523 } 524 525 @MediumTest testBindView_CustomLabel()526 public void testBindView_CustomLabel() { 527 createCallLogEntryWithCachedValues( 528 "John Doe", 529 Phone.TYPE_CUSTOM, 530 TEST_DEFAULT_CUSTOM_LABEL); 531 532 mAdapter.changeCursor(mCursor); 533 mAdapter.onBindViewHolder(mViewHolder, 0); 534 535 assertNameIs(mViewHolder, "John Doe"); 536 assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, TEST_DEFAULT_CUSTOM_LABEL); 537 } 538 539 @MediumTest testBindView_NumberOnlyDbCachedFormattedNumber()540 public void testBindView_NumberOnlyDbCachedFormattedNumber() { 541 createCallLogEntryWithCachedValues( 542 TEST_NUMBER, 543 AppCompatConstants.CALLS_INCOMING_TYPE, 544 EMPTY_STRING, 545 TEST_CACHED_NUMBER_TYPE, 546 TEST_CACHED_NUMBER_LABEL, 547 TEST_FORMATTED_NUMBER, 548 false /* inject */); 549 550 mAdapter.changeCursor(mCursor); 551 mAdapter.onBindViewHolder(mViewHolder, 0); 552 553 assertNameIs(mViewHolder, TEST_FORMATTED_NUMBER); 554 } 555 556 @MediumTest testBindVoicemailPromoCard()557 public void testBindVoicemailPromoCard() { 558 createCallLogEntry(TEST_NUMBER_1); 559 createCallLogEntry(TEST_NUMBER_1); 560 createCallLogEntry(TEST_NUMBER_2); 561 createCallLogEntry(TEST_NUMBER_2); 562 createCallLogEntry(TEST_NUMBER_2); 563 createCallLogEntry(TEST_NUMBER_3); 564 565 // Bind the voicemail promo card. 566 mAdapter.showVoicemailPromoCard(true); 567 mAdapter.changeCursor(mCursor); 568 mAdapter.onBindViewHolder(PromoCardViewHolder.createForTest(getContext()), 0); 569 570 // Check that displaying the promo card does not affect the grouping or list display. 571 mAdapter.onBindViewHolder(mViewHolder, 1); 572 assertEquals(2, mAdapter.getGroupSize(1)); 573 assertEquals(TEST_NUMBER_1, mViewHolder.number); 574 575 mAdapter.onBindViewHolder(mViewHolder, 2); 576 assertEquals(3, mAdapter.getGroupSize(2)); 577 assertEquals(TEST_NUMBER_2, mViewHolder.number); 578 579 mAdapter.onBindViewHolder(mViewHolder, 3); 580 assertEquals(1, mAdapter.getGroupSize(3)); 581 assertEquals(TEST_NUMBER_3, mViewHolder.number); 582 } 583 testVoicemailArchive()584 public void testVoicemailArchive() { 585 setUpArchiveAdapter(); 586 createVoicemailArchiveCallLogEntry(); 587 588 mAdapter.changeCursorVoicemail(mCursor); 589 mAdapter.onBindViewHolder(mViewHolder, 0); 590 591 assertEquals(Uri.parse(mViewHolder.voicemailUri), 592 ContentUris.withAppendedId( 593 VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, 0)); 594 assertNull(mViewHolder.primaryActionButtonView.getTag()); 595 } 596 createCallLogEntry()597 private void createCallLogEntry() { 598 createCallLogEntry(TEST_NUMBER); 599 } 600 createCallLogEntry(String testNumber)601 private void createCallLogEntry(String testNumber) { 602 createCallLogEntry(testNumber, EMPTY_STRING, NO_VALUE_SET, NO_VALUE_SET); 603 } 604 createPrivateCallLogEntry()605 private void createPrivateCallLogEntry() { 606 createCallLogEntry( 607 EMPTY_STRING, 608 EMPTY_STRING, 609 Calls.PRESENTATION_RESTRICTED, 610 AppCompatConstants.CALLS_INCOMING_TYPE); 611 } 612 createUnknownCallLogEntry()613 private void createUnknownCallLogEntry() { 614 createCallLogEntry( 615 EMPTY_STRING, 616 EMPTY_STRING, 617 Calls.PRESENTATION_UNKNOWN, 618 AppCompatConstants.CALLS_INCOMING_TYPE); 619 } 620 createVoicemailCallLogEntry()621 private void createVoicemailCallLogEntry() { 622 createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, Calls.VOICEMAIL_TYPE); 623 } 624 createVoicemailArchiveCallLogEntry()625 private void createVoicemailArchiveCallLogEntry() { 626 createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, ARCHIVE_TYPE); 627 } 628 createCallLogEntry(String number, String postDialDigits, int presentation, int type)629 private void createCallLogEntry(String number, String postDialDigits, int presentation, 630 int type) { 631 Object[] values = getValues(number, postDialDigits, presentation, type); 632 mCursor.addRow(values); 633 } 634 createCallLogEntry(String number, String postDialDigits, String viaNumber, int presentation, int type)635 private void createCallLogEntry(String number, String postDialDigits, String viaNumber, 636 int presentation, int type) { 637 Object[] values = getValues(number, postDialDigits, viaNumber, presentation, type); 638 mCursor.addRow(values); 639 } 640 createCallLogEntryWithCachedValues(boolean inject)641 private void createCallLogEntryWithCachedValues(boolean inject) { 642 createCallLogEntryWithCachedValues( 643 TEST_NUMBER, 644 NO_VALUE_SET, 645 TEST_CACHED_NAME_PRIMARY, 646 TEST_CACHED_NUMBER_TYPE, 647 TEST_CACHED_NUMBER_LABEL, 648 EMPTY_STRING, 649 inject); 650 } 651 createCallLogEntryWithCachedValues( String cachedName, int cachedNumberType, String cachedNumberLabel)652 private void createCallLogEntryWithCachedValues( 653 String cachedName, int cachedNumberType, String cachedNumberLabel) { 654 createCallLogEntryWithCachedValues( 655 TEST_NUMBER, 656 NO_VALUE_SET, 657 cachedName, 658 cachedNumberType, 659 cachedNumberLabel, 660 EMPTY_STRING, 661 false /* inject */); 662 } 663 664 /** 665 * Inserts a new call log entry 666 * 667 * It includes the values for the cached contact associated with the number. 668 * 669 * @param number The phone number. 670 * @param type Valid value of {@code Calls.TYPE}. 671 * @param cachedName The name of the contact with this number 672 * @param cachedNumberType The type of the number, from the contact with this number. 673 * @param cachedNumberLabel The label of the number, from the contact with this number. 674 * @param cachedFormattedNumber The formatted number, from the contact with this number. 675 * @param inject Whether to inject the contact info into the adapter's ContactInfoCache. 676 */ createCallLogEntryWithCachedValues( String number, int type, String cachedName, int cachedNumberType, String cachedNumberLabel, String cachedFormattedNumber, boolean inject)677 private void createCallLogEntryWithCachedValues( 678 String number, 679 int type, 680 String cachedName, 681 int cachedNumberType, 682 String cachedNumberLabel, 683 String cachedFormattedNumber, 684 boolean inject) { 685 Object[] values = getValues(number, EMPTY_STRING, NO_VALUE_SET, type); 686 values[CallLogQuery.CACHED_NAME] = cachedName; 687 values[CallLogQuery.CACHED_NUMBER_TYPE] = cachedNumberType; 688 values[CallLogQuery.CACHED_NUMBER_LABEL] = cachedNumberLabel; 689 values[CallLogQuery.CACHED_FORMATTED_NUMBER] = cachedFormattedNumber; 690 691 mCursor.addRow(values); 692 693 if (inject) { 694 ContactInfo contactInfo = 695 createContactInfo(cachedName, cachedName, cachedNumberType, cachedNumberLabel); 696 mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo); 697 } 698 } 699 700 /** 701 * @param number The phone number. 702 * @param postDialDigits The post dial digits dialed (if any) 703 * @param presentation Number representing display rules for "allowed", 704 * "payphone", "restricted", or "unknown". 705 * @param type The type of the call (outgoing/ingoing) 706 */ getValues( String number, String postDialDigits, int presentation, int type)707 private Object[] getValues( 708 String number, 709 String postDialDigits, 710 int presentation, 711 int type) { 712 return getValues(number, postDialDigits, "", presentation, type); 713 } 714 715 /** 716 * @param number The phone number. 717 * @param postDialDigits The post dial digits dialed (if any) 718 * @param viaNumber The secondary number that the call was placed via 719 * @param presentation Number representing display rules for "allowed", 720 * "payphone", "restricted", or "unknown". 721 * @param type The type of the call (outgoing/ingoing) 722 */ getValues( String number, String postDialDigits, String viaNumber, int presentation, int type)723 private Object[] getValues( 724 String number, 725 String postDialDigits, 726 String viaNumber, 727 int presentation, 728 int type) { 729 Object[] values = CallLogQueryTestUtils.createTestValues(); 730 731 values[CallLogQuery.ID] = mCursor.getCount(); 732 values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO; 733 values[CallLogQuery.DATE] = new Date().getTime(); 734 values[CallLogQuery.DURATION] = mRandom.nextInt(10 * 60); 735 736 if (!TextUtils.isEmpty(number)) { 737 values[CallLogQuery.NUMBER] = number; 738 } 739 if (!TextUtils.isEmpty(postDialDigits) && CompatUtils.isNCompatible()) { 740 values[CallLogQuery.POST_DIAL_DIGITS] = postDialDigits; 741 } 742 if (!TextUtils.isEmpty(viaNumber) && CompatUtils.isNCompatible()) { 743 values[CallLogQuery.VIA_NUMBER] = viaNumber; 744 } 745 if (presentation != NO_VALUE_SET) { 746 values[CallLogQuery.NUMBER_PRESENTATION] = presentation; 747 } 748 if (type != NO_VALUE_SET) { 749 values[CallLogQuery.CALL_TYPE] = type; 750 } 751 if (type == AppCompatConstants.CALLS_VOICEMAIL_TYPE) { 752 values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId( 753 VoicemailContract.Voicemails.CONTENT_URI, mCursor.getCount()); 754 } 755 if (type == ARCHIVE_TYPE) { 756 values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId( 757 VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, mCursor.getCount()); 758 } 759 760 return values; 761 } 762 createContactInfo()763 private ContactInfo createContactInfo() { 764 return createContactInfo( 765 TEST_CACHED_NAME_PRIMARY, 766 TEST_CACHED_NAME_ALTERNATIVE); 767 } 768 createContactInfo(String namePrimary, String nameAlternative)769 private ContactInfo createContactInfo(String namePrimary, String nameAlternative) { 770 return createContactInfo( 771 namePrimary, 772 nameAlternative, 773 TEST_CACHED_NUMBER_TYPE, 774 TEST_CACHED_NUMBER_LABEL); 775 } 776 777 /** Returns a contact info with default values. */ createContactInfo(String namePrimary, String nameAlternative, int type, String label)778 private ContactInfo createContactInfo(String namePrimary, String nameAlternative, int type, String label) { 779 ContactInfo info = new ContactInfo(); 780 info.number = TEST_NUMBER; 781 info.name = namePrimary; 782 info.nameAlternative = nameAlternative; 783 info.type = type; 784 info.label = label; 785 info.formattedNumber = TEST_FORMATTED_NUMBER; 786 info.normalizedNumber = TEST_NUMBER; 787 info.lookupUri = TEST_LOOKUP_URI; 788 return info; 789 } 790 791 // Asserts that the name text view is shown and contains the given text. assertNameIs(CallLogListItemViewHolder viewHolder, String name)792 private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) { 793 assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility()); 794 assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText()); 795 } 796 797 // Asserts that the label text view contains the given text. assertLabel( CallLogListItemViewHolder viewHolder, CharSequence number, CharSequence label)798 private void assertLabel( 799 CallLogListItemViewHolder viewHolder, CharSequence number, CharSequence label) { 800 if (label != null) { 801 assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText() 802 .toString().contains(label)); 803 } 804 } 805 assertHasCallAction(CallLogListItemViewHolder viewHolder)806 private void assertHasCallAction(CallLogListItemViewHolder viewHolder) { 807 assertHasCallActionToGivenNumber(viewHolder, TEST_NUMBER); 808 } 809 assertHasCallActionToGivenNumber(CallLogListItemViewHolder viewHolder, String number)810 private void assertHasCallActionToGivenNumber(CallLogListItemViewHolder viewHolder, 811 String number) { 812 IntentProvider intentProvider = 813 (IntentProvider) viewHolder.primaryActionButtonView.getTag(); 814 Intent intent = intentProvider.getIntent(getContext()); 815 assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction()); 816 assertEquals(Uri.parse("tel:" + Uri.encode(number)), intent.getData()); 817 } 818 819 /** Returns the label associated with a given phone type. */ getTypeLabel(int phoneType)820 private CharSequence getTypeLabel(int phoneType) { 821 return Phone.getTypeLabel(getContext().getResources(), phoneType, ""); 822 } 823 setUpArchiveAdapter()824 private void setUpArchiveAdapter() { 825 // Use a call fetcher that does not do anything. 826 CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() { 827 @Override 828 public void fetchCalls() {} 829 }; 830 831 ContactInfoHelper fakeContactInfoHelper = 832 new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) { 833 @Override 834 public ContactInfo lookupNumber(String number, String countryIso) { 835 ContactInfo info = new ContactInfo(); 836 info.number = number; 837 info.formattedNumber = number; 838 return info; 839 } 840 }; 841 842 mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper, 843 CallLogAdapter.ACTIVITY_TYPE_ARCHIVE); 844 } 845 846 /// Subclass of {@link CallLogAdapter} used in tests to intercept certain calls. 847 private static final class TestCallLogAdapter extends CallLogAdapter { TestCallLogAdapter(Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper, int mActivity)848 public TestCallLogAdapter(Context context, CallFetcher callFetcher, 849 ContactInfoHelper contactInfoHelper, int mActivity) { 850 super(context, callFetcher, contactInfoHelper, null, 851 mActivity); 852 mContactInfoCache = new TestContactInfoCache( 853 contactInfoHelper, mOnContactInfoChangedListener); 854 } 855 getContactInfoCache()856 public TestContactInfoCache getContactInfoCache() { 857 return (TestContactInfoCache) mContactInfoCache; 858 } 859 showVoicemailPromoCard(boolean show)860 public void showVoicemailPromoCard(boolean show) { 861 mShowVoicemailPromoCard = show; 862 } 863 } 864 865 private static final class TestContactInfoCache extends ContactInfoCache { 866 public static class Request { 867 public final String number; 868 public final String countryIso; 869 public final ContactInfo callLogInfo; 870 public final boolean immediate; 871 Request(String number, String countryIso, ContactInfo callLogInfo, boolean immediate)872 public Request(String number, String countryIso, ContactInfo callLogInfo, 873 boolean immediate) { 874 this.number = number; 875 this.countryIso = countryIso; 876 this.callLogInfo = callLogInfo; 877 this.immediate = immediate; 878 } 879 } 880 881 public final List<Request> requests = Lists.newArrayList(); 882 883 /** 884 * Dummy contactInfo to return in the even that the getValue method has been mocked 885 */ 886 private ContactInfo mContactInfo; 887 TestContactInfoCache( ContactInfoHelper contactInfoHelper, OnContactInfoChangedListener listener)888 public TestContactInfoCache( 889 ContactInfoHelper contactInfoHelper, OnContactInfoChangedListener listener) { 890 super(contactInfoHelper, listener); 891 } 892 893 /** 894 * Sets the given value to be returned by all calls to 895 * {@link #getValue(String, String, ContactInfo)} 896 * 897 * @param contactInfo the contactInfo 898 */ mockGetValue(ContactInfo contactInfo)899 public void mockGetValue(ContactInfo contactInfo) { 900 this.mContactInfo = contactInfo; 901 } 902 903 @Override getValue(String number, String countryIso, ContactInfo cachedContactInfo)904 public ContactInfo getValue(String number, String countryIso, 905 ContactInfo cachedContactInfo) { 906 if (mContactInfo != null) { 907 return mContactInfo; 908 } 909 return super.getValue(number, countryIso, cachedContactInfo); 910 } 911 912 @Override enqueueRequest(String number, String countryIso, ContactInfo callLogInfo, boolean immediate)913 protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo, 914 boolean immediate) { 915 requests.add(new Request(number, countryIso, callLogInfo, immediate)); 916 } 917 } 918 } 919