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.dialer.calllog; 18 19 import android.app.FragmentManager; 20 import android.app.FragmentTransaction; 21 import android.content.ComponentName; 22 import android.content.ContentUris; 23 import android.content.Intent; 24 import android.content.res.Resources; 25 import android.database.MatrixCursor; 26 import android.graphics.Bitmap; 27 import android.graphics.drawable.BitmapDrawable; 28 import android.net.Uri; 29 import android.provider.CallLog.Calls; 30 import android.provider.ContactsContract.CommonDataKinds.Phone; 31 import android.provider.VoicemailContract; 32 import android.telephony.PhoneNumberUtils; 33 import android.telephony.TelephonyManager; 34 import android.test.ActivityInstrumentationTestCase2; 35 import android.test.suitebuilder.annotation.LargeTest; 36 import android.test.suitebuilder.annotation.MediumTest; 37 import android.view.View; 38 import android.widget.FrameLayout; 39 40 import com.android.contacts.common.test.FragmentTestActivity; 41 import com.android.dialer.CallDetailActivity; 42 import com.android.dialer.R; 43 44 import java.util.Date; 45 import java.util.Formatter; 46 import java.util.HashMap; 47 import java.util.Random; 48 49 /** 50 * Tests for the contact call list activity. 51 * 52 * Running all tests: 53 * 54 * runtest contacts 55 * or 56 * adb shell am instrument \ 57 * -w com.android.contacts.tests/android.test.InstrumentationTestRunner 58 */ 59 @LargeTest 60 public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<FragmentTestActivity> { 61 private static final int RAND_DURATION = -1; 62 private static final long NOW = -1L; 63 64 /** A test value for the URI of a contact. */ 65 private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2"); 66 /** A test value for the country ISO of the phone number in the call log. */ 67 private static final String TEST_COUNTRY_ISO = "US"; 68 /** A phone number to be used in tests. */ 69 private static final String TEST_NUMBER = "12125551000"; 70 /** The formatted version of {@link #TEST_NUMBER}. */ 71 private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000"; 72 73 private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel"; 74 75 /** The activity in which we are hosting the fragment. */ 76 private FragmentTestActivity mActivity; 77 private CallLogFragment mFragment; 78 private FrameLayout mParentView; 79 /** 80 * The adapter used by the fragment to build the rows in the call log. We use it with our own in 81 * memory database. 82 */ 83 private CallLogAdapter mAdapter; 84 private String mVoicemail; 85 86 // In memory array to hold the rows corresponding to the 'calls' table. 87 private MatrixCursor mCursor; 88 private int mIndex; // Of the next row. 89 90 private Random mRnd; 91 92 // References to the icons bitmaps used to build the list are stored in a 93 // map mIcons. The keys to retrieve the icons are: 94 // Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE and Calls.MISSED_TYPE. 95 private HashMap<Integer, Bitmap> mCallTypeIcons; 96 97 // An item in the call list. All the methods performing checks use it. 98 private CallLogListItemViews mItem; 99 // The list of views representing the data in the DB. View are in 100 // reverse order compare to the DB. 101 private View[] mList; 102 103 public CallLogFragmentTest() { 104 super("com.android.dialer", FragmentTestActivity.class); 105 mIndex = 1; 106 mRnd = new Random(); 107 } 108 109 @Override 110 public void setUp() { 111 mActivity = getActivity(); 112 // Needed by the CallLogFragment. 113 mActivity.setTheme(R.style.DialtactsTheme); 114 115 // Create the fragment and load it into the activity. 116 mFragment = new CallLogFragment(); 117 FragmentManager fragmentManager = mActivity.getFragmentManager(); 118 FragmentTransaction transaction = fragmentManager.beginTransaction(); 119 transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment); 120 transaction.commitAllowingStateLoss(); 121 // Wait for the fragment to be loaded. 122 getInstrumentation().waitForIdleSync(); 123 124 mVoicemail = TelephonyManager.getDefault().getVoiceMailNumber(); 125 mAdapter = mFragment.getAdapter(); 126 // Do not process requests for details during tests. This would start a background thread, 127 // which makes the tests flaky. 128 mAdapter.disableRequestProcessingForTest(); 129 mAdapter.stopRequestProcessing(); 130 mParentView = new FrameLayout(mActivity); 131 mCursor = new MatrixCursor(CallLogQuery._PROJECTION); 132 buildIconMap(); 133 } 134 135 /** 136 * Checks that the call icon is not visible for private and 137 * unknown numbers. 138 * Use 2 passes, one where new views are created and one where 139 * half of the total views are updated and the other half created. 140 */ 141 @MediumTest 142 public void testCallViewIsNotVisibleForPrivateAndUnknownNumbers() { 143 final int SIZE = 100; 144 mList = new View[SIZE]; 145 146 // Insert the first batch of entries. 147 mCursor.moveToFirst(); 148 insertRandomEntries(SIZE / 2); 149 int startOfSecondBatch = mCursor.getPosition(); 150 151 buildViewListFromDb(); 152 checkCallStatus(); 153 154 // Append the rest of the entries. We keep the first set of 155 // views around so they get updated and not built from 156 // scratch, this exposes some bugs that are not there when the 157 // call log is launched for the 1st time but show up when the 158 // call log gets updated afterwards. 159 mCursor.move(startOfSecondBatch); 160 insertRandomEntries(SIZE / 2); 161 162 buildViewListFromDb(); 163 checkCallStatus(); 164 } 165 166 @MediumTest 167 public void testCallAndGroupViews_GroupView() { 168 mCursor.moveToFirst(); 169 insertPrivate(NOW, 0); 170 insertPrivate(NOW, 0); 171 insertPrivate(NOW, 0); 172 View view = mAdapter.newGroupView(getActivity(), mParentView); 173 mAdapter.bindGroupView(view, getActivity(), mCursor, 3, false); 174 assertNotNull(view.findViewById(R.id.secondary_action_icon)); 175 } 176 177 @MediumTest 178 public void testCallAndGroupViews_StandAloneView() { 179 mCursor.moveToFirst(); 180 insertPrivate(NOW, 0); 181 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 182 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 183 assertNotNull(view.findViewById(R.id.secondary_action_icon)); 184 } 185 186 @MediumTest 187 public void testCallAndGroupViews_ChildView() { 188 mCursor.moveToFirst(); 189 insertPrivate(NOW, 0); 190 View view = mAdapter.newChildView(getActivity(), mParentView); 191 mAdapter.bindChildView(view, getActivity(), mCursor); 192 assertNotNull(view.findViewById(R.id.secondary_action_icon)); 193 } 194 195 @MediumTest 196 public void testBindView_NumberOnlyNoCache() { 197 mCursor.moveToFirst(); 198 insert(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE); 199 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 200 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 201 202 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 203 assertNameIs(views, TEST_NUMBER); 204 } 205 206 @MediumTest 207 public void testBindView_NumberOnlyDbCachedFormattedNumber() { 208 mCursor.moveToFirst(); 209 Object[] values = getValuesToInsert(TEST_NUMBER, 210 Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE); 211 values[CallLogQuery.CACHED_FORMATTED_NUMBER] = TEST_FORMATTED_NUMBER; 212 insertValues(values); 213 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 214 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 215 216 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 217 assertNameIs(views, TEST_FORMATTED_NUMBER); 218 } 219 220 @MediumTest 221 public void testBindView_WithCachedName() { 222 mCursor.moveToFirst(); 223 // provide a default custom label instead of an empty string, which corresponds to 224 // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL} 225 insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, 226 "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); 227 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 228 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 229 230 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 231 assertNameIs(views, "John Doe"); 232 assertLabel(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); 233 } 234 235 @MediumTest 236 public void testBindView_UriNumber() { 237 mCursor.moveToFirst(); 238 insertWithCachedValues("sip:johndoe (at) gmail.com", NOW, 0, Calls.INCOMING_TYPE, 239 "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); 240 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 241 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 242 243 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 244 assertNameIs(views, "John Doe"); 245 assertLabel(views, "sip:johndoe (at) gmail.com", "sip:johndoe (at) gmail.com"); 246 } 247 248 @MediumTest 249 public void testBindView_HomeLabel() { 250 mCursor.moveToFirst(); 251 // provide a default custom label instead of an empty string, which corresponds to 252 // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL} 253 insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, 254 "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); 255 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 256 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 257 258 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 259 assertNameIs(views, "John Doe"); 260 assertLabel(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); 261 } 262 263 @MediumTest 264 public void testBindView_WorkLabel() { 265 mCursor.moveToFirst(); 266 // provide a default custom label instead of an empty string, which corresponds to 267 // {@link com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL} 268 insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, 269 "John Doe", Phone.TYPE_WORK, TEST_DEFAULT_CUSTOM_LABEL); 270 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 271 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 272 273 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 274 assertNameIs(views, "John Doe"); 275 assertLabel(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK)); 276 } 277 278 @MediumTest 279 public void testBindView_CustomLabel() { 280 mCursor.moveToFirst(); 281 String numberLabel = "My label"; 282 insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, 283 "John Doe", Phone.TYPE_CUSTOM, numberLabel); 284 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 285 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 286 287 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 288 assertNameIs(views, "John Doe"); 289 assertLabel(views, TEST_FORMATTED_NUMBER, numberLabel); 290 } 291 292 @MediumTest 293 public void testBindView_WithQuickContactBadge() { 294 mCursor.moveToFirst(); 295 insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, 296 "John Doe", Phone.TYPE_HOME, ""); 297 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 298 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 299 300 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 301 assertTrue(views.quickContactView.isEnabled()); 302 } 303 304 @MediumTest 305 public void testBindView_WithoutQuickContactBadge() { 306 mCursor.moveToFirst(); 307 insert(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE); 308 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 309 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 310 311 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 312 assertFalse(views.quickContactView.isEnabled()); 313 } 314 315 @MediumTest 316 public void testBindView_CallButton() { 317 mCursor.moveToFirst(); 318 insert(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE); 319 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 320 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 321 322 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 323 324 // The primaryActionView tag is set in the 325 // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method. If it is possible 326 // to place a call to the phone number, a call intent will have been created for the 327 // primaryActionView. 328 IntentProvider intentProvider = (IntentProvider) views.primaryActionView.getTag(); 329 Intent intent = intentProvider.getIntent(mActivity); 330 // Starts a call. 331 assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction()); 332 // To the entry's number. 333 assertEquals(Uri.parse("tel:" + TEST_NUMBER), intent.getData()); 334 } 335 336 @MediumTest 337 public void testBindView_PlayButton() { 338 mCursor.moveToFirst(); 339 insertVoicemail(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, NOW, 0); 340 View view = mAdapter.newStandAloneView(getActivity(), mParentView); 341 mAdapter.bindStandAloneView(view, getActivity(), mCursor); 342 343 CallLogListItemViews views = (CallLogListItemViews) view.getTag(); 344 IntentProvider intentProvider = (IntentProvider) views.secondaryActionButtonView.getTag(); 345 Intent intent = intentProvider.getIntent(mActivity); 346 // Starts the call detail activity. 347 assertEquals(new ComponentName(mActivity, CallDetailActivity.class), 348 intent.getComponent()); 349 // With the given entry. 350 assertEquals(ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, 1), 351 intent.getData()); 352 // With the URI of the voicemail. 353 assertEquals( 354 ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, 1), 355 intent.getParcelableExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI)); 356 // And starts playback. 357 assertTrue( 358 intent.getBooleanExtra(CallDetailActivity.EXTRA_VOICEMAIL_START_PLAYBACK, false)); 359 } 360 361 /** Returns the label associated with a given phone type. */ 362 private CharSequence getTypeLabel(int phoneType) { 363 return Phone.getTypeLabel(getActivity().getResources(), phoneType, ""); 364 } 365 366 // 367 // HELPERS to check conditions on the DB/views 368 // 369 /** 370 * Go over the views in the list and check to ensure that 371 * callable numbers have an associated call intent, where numbers 372 * which are not callable have a null intent. 373 */ 374 private void checkCallStatus() { 375 for (int i = 0; i < mList.length; i++) { 376 if (null == mList[i]) { 377 break; 378 } 379 mItem = (CallLogListItemViews) mList[i].getTag(); 380 int presentation = getPhoneNumberPresentationForListEntry(i); 381 if (presentation == Calls.PRESENTATION_RESTRICTED || 382 presentation == Calls.PRESENTATION_UNKNOWN) { 383 //If number is not callable, the primary action view should have a null tag. 384 assertNull(mItem.primaryActionView.getTag()); 385 } else { 386 //If the number is callable, the primary action view should have a non-null tag. 387 assertNotNull(mItem.primaryActionView.getTag()); 388 389 IntentProvider intentProvider = (IntentProvider)mItem.primaryActionView.getTag(); 390 Intent callIntent = intentProvider.getIntent(mActivity); 391 392 //The intent should be to make the call 393 assertEquals(Intent.ACTION_CALL_PRIVILEGED, callIntent.getAction()); 394 } 395 } 396 } 397 398 399 // 400 // HELPERS to setup the tests. 401 // 402 403 /** 404 * Get the Bitmap from the icons in the contacts package. 405 */ 406 private Bitmap getBitmap(String resName) { 407 Resources r = mActivity.getResources(); 408 int resid = r.getIdentifier(resName, "drawable", "com.android.dialer"); 409 BitmapDrawable d = (BitmapDrawable) r.getDrawable(resid); 410 assertNotNull(d); 411 return d.getBitmap(); 412 } 413 414 /** 415 * Fetch all the icons we need in tests from the contacts app and store them in a map. 416 */ 417 private void buildIconMap() { 418 mCallTypeIcons = new HashMap<Integer, Bitmap>(3); 419 420 mCallTypeIcons.put(Calls.INCOMING_TYPE, getBitmap("ic_call_incoming_holo_dark")); 421 mCallTypeIcons.put(Calls.MISSED_TYPE, getBitmap("ic_call_missed_holo_dark")); 422 mCallTypeIcons.put(Calls.OUTGOING_TYPE, getBitmap("ic_call_outgoing_holo_dark")); 423 } 424 425 // 426 // HELPERS to build/update the call entries (views) from the DB. 427 // 428 429 /** 430 * Read the DB and foreach call either update the existing view if 431 * one exists already otherwise create one. 432 * The list is build from a DESC view of the DB (last inserted entry is first). 433 */ 434 private void buildViewListFromDb() { 435 int i = 0; 436 mCursor.moveToLast(); 437 while(!mCursor.isBeforeFirst()) { 438 if (null == mList[i]) { 439 mList[i] = mAdapter.newStandAloneView(mActivity, mParentView); 440 } 441 mAdapter.bindStandAloneView(mList[i], mActivity, mCursor); 442 mCursor.moveToPrevious(); 443 i++; 444 } 445 } 446 447 /** Returns the number presentation associated with the given entry in {{@link #mList}. */ 448 private int getPhoneNumberPresentationForListEntry(int index) { 449 // The entries are added backward, so count from the end of the cursor. 450 mCursor.moveToPosition(mCursor.getCount() - index - 1); 451 return mCursor.getInt(CallLogQuery.NUMBER_PRESENTATION); 452 } 453 454 // 455 // HELPERS to insert numbers in the call log DB. 456 // 457 458 /** 459 * Insert a certain number of random numbers in the DB. Makes sure 460 * there is at least one private and one unknown number in the DB. 461 * @param num Of entries to be inserted. 462 */ 463 private void insertRandomEntries(int num) { 464 if (num < 10) { 465 throw new IllegalArgumentException("num should be >= 10"); 466 } 467 boolean privateOrUnknownOrVm[]; 468 privateOrUnknownOrVm = insertRandomRange(0, num - 2); 469 470 if (privateOrUnknownOrVm[0] && privateOrUnknownOrVm[1]) { 471 insertRandomRange(num - 2, num); 472 } else { 473 insertPrivate(NOW, RAND_DURATION); 474 insertUnknown(NOW, RAND_DURATION); 475 } 476 } 477 478 /** 479 * Insert a new call entry in the test DB. 480 * 481 * It includes the values for the cached contact associated with the number. 482 * 483 * @param number The phone number. 484 * @param date In millisec since epoch. Use NOW to use the current time. 485 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 486 * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. 487 * @param cachedName the name of the contact with this number 488 * @param cachedNumberType the type of the number, from the contact with this number 489 * @param cachedNumberLabel the label of the number, from the contact with this number 490 */ 491 private void insertWithCachedValues(String number, long date, int duration, int type, 492 String cachedName, int cachedNumberType, String cachedNumberLabel) { 493 insert(number, Calls.PRESENTATION_ALLOWED, date, duration, type); 494 ContactInfo contactInfo = new ContactInfo(); 495 contactInfo.lookupUri = TEST_LOOKUP_URI; 496 contactInfo.name = cachedName; 497 contactInfo.type = cachedNumberType; 498 contactInfo.label = cachedNumberLabel; 499 String formattedNumber = PhoneNumberUtils.formatNumber(number, TEST_COUNTRY_ISO); 500 if (formattedNumber == null) { 501 formattedNumber = number; 502 } 503 contactInfo.formattedNumber = formattedNumber; 504 contactInfo.normalizedNumber = number; 505 contactInfo.photoId = 0; 506 mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo); 507 } 508 509 /** 510 * Insert a new call entry in the test DB. 511 * @param number The phone number. 512 * @param presentation Number representing display rules for "allowed", 513 * "payphone", "restricted", or "unknown". 514 * @param date In millisec since epoch. Use NOW to use the current time. 515 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 516 * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. 517 */ 518 private void insert(String number, int presentation, long date, int duration, int type) { 519 insertValues(getValuesToInsert(number, presentation, date, duration, type)); 520 } 521 522 /** Inserts the given values in the cursor. */ 523 private void insertValues(Object[] values) { 524 mCursor.addRow(values); 525 ++mIndex; 526 } 527 528 /** 529 * Returns the values for a new call entry. 530 * 531 * @param number The phone number. 532 * @param presentation Number representing display rules for "allowed", 533 * "payphone", "restricted", or "unknown". 534 * @param date In millisec since epoch. Use NOW to use the current time. 535 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 536 * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. 537 */ 538 private Object[] getValuesToInsert(String number, int presentation, 539 long date, int duration, int type) { 540 Object[] values = CallLogQueryTestUtils.createTestValues(); 541 values[CallLogQuery.ID] = mIndex; 542 values[CallLogQuery.NUMBER] = number; 543 values[CallLogQuery.NUMBER_PRESENTATION] = presentation; 544 values[CallLogQuery.DATE] = date == NOW ? new Date().getTime() : date; 545 values[CallLogQuery.DURATION] = duration < 0 ? mRnd.nextInt(10 * 60) : duration; 546 if (mVoicemail != null && mVoicemail.equals(number)) { 547 assertEquals(Calls.OUTGOING_TYPE, type); 548 } 549 values[CallLogQuery.CALL_TYPE] = type; 550 values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO; 551 return values; 552 } 553 554 /** 555 * Insert a new voicemail entry in the test DB. 556 * @param number The phone number. 557 * @param presentation Number representing display rules for "allowed", 558 * "payphone", "restricted", or "unknown". 559 * @param date In millisec since epoch. Use NOW to use the current time. 560 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 561 */ 562 private void insertVoicemail(String number, int presentation, long date, int duration) { 563 Object[] values = getValuesToInsert(number, presentation, date, duration, Calls.VOICEMAIL_TYPE); 564 // Must have the same index as the row. 565 values[CallLogQuery.VOICEMAIL_URI] = 566 ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, mIndex); 567 insertValues(values); 568 } 569 570 /** 571 * Insert a new private call entry in the test DB. 572 * @param date In millisec since epoch. Use NOW to use the current time. 573 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 574 */ 575 private void insertPrivate(long date, int duration) { 576 insert("", Calls.PRESENTATION_RESTRICTED, date, duration, Calls.INCOMING_TYPE); 577 } 578 579 /** 580 * Insert a new unknown call entry in the test DB. 581 * @param date In millisec since epoch. Use NOW to use the current time. 582 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 583 */ 584 private void insertUnknown(long date, int duration) { 585 insert("", Calls.PRESENTATION_UNKNOWN, date, duration, Calls.INCOMING_TYPE); 586 } 587 588 /** 589 * Insert a new call to voicemail entry in the test DB. 590 * @param date In millisec since epoch. Use NOW to use the current time. 591 * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. 592 */ 593 private void insertCalltoVoicemail(long date, int duration) { 594 // mVoicemail may be null 595 if (mVoicemail != null) { 596 insert(mVoicemail, Calls.PRESENTATION_ALLOWED, date, duration, Calls.OUTGOING_TYPE); 597 } 598 } 599 600 /** 601 * Insert a range [start, end) of random numbers in the DB. For 602 * each row, there is a 1/10 probability that the number will be 603 * marked as PRIVATE or UNKNOWN or VOICEMAIL. For regular numbers, a number is 604 * inserted, its last 4 digits will be the number of the iteration 605 * in the range. 606 * @param start Of the range. 607 * @param end Of the range (excluded). 608 * @return An array with 2 booleans [0 = private number, 1 = 609 * unknown number, 2 = voicemail] to indicate if at least one 610 * private or unknown or voicemail number has been inserted. Since 611 * the numbers are random some tests may want to enforce the 612 * insertion of such numbers. 613 */ 614 // TODO: Should insert numbers with contact entries too. 615 private boolean[] insertRandomRange(int start, int end) { 616 boolean[] privateOrUnknownOrVm = new boolean[] {false, false, false}; 617 618 for (int i = start; i < end; i++ ) { 619 int type = mRnd.nextInt(10); 620 621 if (0 == type) { 622 insertPrivate(NOW, RAND_DURATION); 623 privateOrUnknownOrVm[0] = true; 624 } else if (1 == type) { 625 insertUnknown(NOW, RAND_DURATION); 626 privateOrUnknownOrVm[1] = true; 627 } else if (2 == type) { 628 insertCalltoVoicemail(NOW, RAND_DURATION); 629 privateOrUnknownOrVm[2] = true; 630 } else { 631 int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE : Calls.INCOMING_TYPE; 632 final Formatter formatter = new Formatter(); 633 String number = formatter.format("1800123%04d", i).toString(); 634 formatter.close(); 635 insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout); 636 } 637 } 638 return privateOrUnknownOrVm; 639 } 640 641 /** Asserts that the name text view is shown and contains the given text. */ 642 private void assertNameIs(CallLogListItemViews views, String name) { 643 assertEquals(View.VISIBLE, views.phoneCallDetailsViews.nameView.getVisibility()); 644 assertEquals(name, views.phoneCallDetailsViews.nameView.getText()); 645 } 646 647 /** Asserts that the label text view contains the given text. */ 648 private void assertLabel(CallLogListItemViews views, CharSequence number, 649 CharSequence label) { 650 assertEquals(label == null ? View.GONE : View.VISIBLE, 651 views.phoneCallDetailsViews.labelView.getVisibility()); 652 if (label != null) { 653 assertEquals(label, views.phoneCallDetailsViews.labelView.getText().toString()); 654 } 655 } 656 } 657