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