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