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.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     /** The activity in which we are hosting the fragment. */
     74     private FragmentTestActivity mActivity;
     75     private CallLogFragment mFragment;
     76     private FrameLayout mParentView;
     77     /**
     78      * The adapter used by the fragment to build the rows in the call log. We use it with our own in
     79      * memory database.
     80      */
     81     private CallLogAdapter mAdapter;
     82     private String mVoicemail;
     83 
     84     // In memory array to hold the rows corresponding to the 'calls' table.
     85     private MatrixCursor mCursor;
     86     private int mIndex;  // Of the next row.
     87 
     88     private Random mRnd;
     89 
     90     // References to the icons bitmaps used to build the list are stored in a
     91     // map mIcons. The keys to retrieve the icons are:
     92     // Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE and Calls.MISSED_TYPE.
     93     private HashMap<Integer, Bitmap> mCallTypeIcons;
     94 
     95     // An item in the call list. All the methods performing checks use it.
     96     private CallLogListItemViews mItem;
     97     // The list of views representing the data in the DB. View are in
     98     // reverse order compare to the DB.
     99     private View[] mList;
    100 
    101     public CallLogFragmentTest() {
    102         super("com.android.dialer", FragmentTestActivity.class);
    103         mIndex = 1;
    104         mRnd = new Random();
    105     }
    106 
    107     @Override
    108     public void setUp() {
    109         mActivity = getActivity();
    110         // Needed by the CallLogFragment.
    111         mActivity.setTheme(R.style.DialtactsTheme);
    112 
    113         // Create the fragment and load it into the activity.
    114         mFragment = new CallLogFragment();
    115         FragmentManager fragmentManager = mActivity.getFragmentManager();
    116         FragmentTransaction transaction = fragmentManager.beginTransaction();
    117         transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment);
    118         transaction.commit();
    119         // Wait for the fragment to be loaded.
    120         getInstrumentation().waitForIdleSync();
    121 
    122         mVoicemail = TelephonyManager.getDefault().getVoiceMailNumber();
    123         mAdapter = mFragment.getAdapter();
    124         // Do not process requests for details during tests. This would start a background thread,
    125         // which makes the tests flaky.
    126         mAdapter.disableRequestProcessingForTest();
    127         mAdapter.stopRequestProcessing();
    128         mParentView = new FrameLayout(mActivity);
    129         mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
    130         buildIconMap();
    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         assertNotNull(view.findViewById(R.id.secondary_action_icon));
    173     }
    174 
    175     @MediumTest
    176     public void testCallAndGroupViews_StandAloneView() {
    177         mCursor.moveToFirst();
    178         insertPrivate(NOW, 0);
    179         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
    180         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
    181         assertNotNull(view.findViewById(R.id.secondary_action_icon));
    182     }
    183 
    184     @MediumTest
    185     public void testCallAndGroupViews_ChildView() {
    186         mCursor.moveToFirst();
    187         insertPrivate(NOW, 0);
    188         View view = mAdapter.newChildView(getActivity(), mParentView);
    189         mAdapter.bindChildView(view, getActivity(), mCursor);
    190         assertNotNull(view.findViewById(R.id.secondary_action_icon));
    191     }
    192 
    193     @MediumTest
    194     public void testBindView_NumberOnlyNoCache() {
    195         mCursor.moveToFirst();
    196         insert(TEST_NUMBER, Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE);
    197         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
    198         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
    199 
    200         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
    201         assertNameIs(views, TEST_NUMBER);
    202     }
    203 
    204     @MediumTest
    205     public void testBindView_NumberOnlyDbCachedFormattedNumber() {
    206         mCursor.moveToFirst();
    207         Object[] values = getValuesToInsert(TEST_NUMBER,
    208                 Calls.PRESENTATION_ALLOWED, 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         assertLabel(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         assertLabel(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         assertLabel(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         assertLabel(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         assertLabel(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, Calls.PRESENTATION_ALLOWED, 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, Calls.PRESENTATION_ALLOWED, 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, Calls.PRESENTATION_ALLOWED, 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             int presentation = getPhoneNumberPresentationForListEntry(i);
    367             if (presentation == Calls.PRESENTATION_RESTRICTED ||
    368                     presentation == Calls.PRESENTATION_UNKNOWN) {
    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.dialer");
    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 presentation associated with the given entry in {{@link #mList}. */
    426     private int getPhoneNumberPresentationForListEntry(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.getInt(CallLogQuery.NUMBER_PRESENTATION);
    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.
    462      * @param date In millisec since epoch. Use NOW to use the current time.
    463      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
    464      * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
    465      * @param cachedName the name of the contact with this number
    466      * @param cachedNumberType the type of the number, from the contact with this number
    467      * @param cachedNumberLabel the label of the number, from the contact with this number
    468      */
    469     private void insertWithCachedValues(String number, long date, int duration, int type,
    470             String cachedName, int cachedNumberType, String cachedNumberLabel) {
    471         insert(number, Calls.PRESENTATION_ALLOWED, date, duration, type);
    472         ContactInfo contactInfo = new ContactInfo();
    473         contactInfo.lookupUri = TEST_LOOKUP_URI;
    474         contactInfo.name = cachedName;
    475         contactInfo.type = cachedNumberType;
    476         contactInfo.label = cachedNumberLabel;
    477         String formattedNumber = PhoneNumberUtils.formatNumber(number, TEST_COUNTRY_ISO);
    478         if (formattedNumber == null) {
    479             formattedNumber = number;
    480         }
    481         contactInfo.formattedNumber = formattedNumber;
    482         contactInfo.normalizedNumber = number;
    483         contactInfo.photoId = 0;
    484         mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo);
    485     }
    486 
    487     /**
    488      * Insert a new call entry in the test DB.
    489      * @param number The phone number.
    490      * @param presentation Number representing display rules for "allowed",
    491      *               "payphone", "restricted", or "unknown".
    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, int presentation, long date, int duration, int type) {
    497         insertValues(getValuesToInsert(number, presentation, 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.
    510      * @param presentation Number representing display rules for "allowed",
    511      *               "payphone", "restricted", or "unknown".
    512      * @param date In millisec since epoch. Use NOW to use the current time.
    513      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
    514      * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
    515      */
    516     private Object[] getValuesToInsert(String number, int presentation,
    517             long date, int duration, int type) {
    518         Object[] values = CallLogQueryTestUtils.createTestValues();
    519         values[CallLogQuery.ID] = mIndex;
    520         values[CallLogQuery.NUMBER] = number;
    521         values[CallLogQuery.NUMBER_PRESENTATION] = presentation;
    522         values[CallLogQuery.DATE] = date == NOW ? new Date().getTime() : date;
    523         values[CallLogQuery.DURATION] = duration < 0 ? mRnd.nextInt(10 * 60) : duration;
    524         if (mVoicemail != null && mVoicemail.equals(number)) {
    525             assertEquals(Calls.OUTGOING_TYPE, type);
    526         }
    527         values[CallLogQuery.CALL_TYPE] = type;
    528         values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
    529         return values;
    530     }
    531 
    532     /**
    533      * Insert a new voicemail entry in the test DB.
    534      * @param number The phone number.
    535      * @param presentation Number representing display rules for "allowed",
    536      *               "payphone", "restricted", or "unknown".
    537      * @param date In millisec since epoch. Use NOW to use the current time.
    538      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
    539      */
    540     private void insertVoicemail(String number, int presentation, long date, int duration) {
    541         Object[] values = getValuesToInsert(number, presentation, date, duration, Calls.VOICEMAIL_TYPE);
    542         // Must have the same index as the row.
    543         values[CallLogQuery.VOICEMAIL_URI] =
    544                 ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, mIndex);
    545         insertValues(values);
    546     }
    547 
    548     /**
    549      * Insert a new private call entry in the test DB.
    550      * @param date In millisec since epoch. Use NOW to use the current time.
    551      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
    552      */
    553     private void insertPrivate(long date, int duration) {
    554         insert("", Calls.PRESENTATION_RESTRICTED, date, duration, Calls.INCOMING_TYPE);
    555     }
    556 
    557     /**
    558      * Insert a new unknown call entry in the test DB.
    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 insertUnknown(long date, int duration) {
    563         insert("", Calls.PRESENTATION_UNKNOWN, date, duration, Calls.INCOMING_TYPE);
    564     }
    565 
    566     /**
    567      * Insert a new call to voicemail entry in the test DB.
    568      * @param date In millisec since epoch. Use NOW to use the current time.
    569      * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
    570      */
    571     private void insertCalltoVoicemail(long date, int duration) {
    572         // mVoicemail may be null
    573         if (mVoicemail != null) {
    574             insert(mVoicemail, Calls.PRESENTATION_ALLOWED, date, duration, Calls.OUTGOING_TYPE);
    575         }
    576     }
    577 
    578     /**
    579      * Insert a range [start, end) of random numbers in the DB. For
    580      * each row, there is a 1/10 probability that the number will be
    581      * marked as PRIVATE or UNKNOWN or VOICEMAIL. For regular numbers, a number is
    582      * inserted, its last 4 digits will be the number of the iteration
    583      * in the range.
    584      * @param start Of the range.
    585      * @param end Of the range (excluded).
    586      * @return An array with 2 booleans [0 = private number, 1 =
    587      * unknown number, 2 = voicemail] to indicate if at least one
    588      * private or unknown or voicemail number has been inserted. Since
    589      * the numbers are random some tests may want to enforce the
    590      * insertion of such numbers.
    591      */
    592     // TODO: Should insert numbers with contact entries too.
    593     private boolean[] insertRandomRange(int start, int end) {
    594         boolean[] privateOrUnknownOrVm = new boolean[] {false, false, false};
    595 
    596         for (int i = start; i < end; i++ ) {
    597             int type = mRnd.nextInt(10);
    598 
    599             if (0 == type) {
    600                 insertPrivate(NOW, RAND_DURATION);
    601                 privateOrUnknownOrVm[0] = true;
    602             } else if (1 == type) {
    603                 insertUnknown(NOW, RAND_DURATION);
    604                 privateOrUnknownOrVm[1] = true;
    605             } else if (2 == type) {
    606                 insertCalltoVoicemail(NOW, RAND_DURATION);
    607                 privateOrUnknownOrVm[2] = true;
    608             } else {
    609                 int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE :  Calls.INCOMING_TYPE;
    610                 String number = new Formatter().format("1800123%04d", i).toString();
    611                 insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout);
    612             }
    613         }
    614         return privateOrUnknownOrVm;
    615     }
    616 
    617     /** Asserts that the name text view is shown and contains the given text. */
    618     private void assertNameIs(CallLogListItemViews views, String name) {
    619         assertEquals(View.VISIBLE, views.phoneCallDetailsViews.nameView.getVisibility());
    620         assertEquals(name, views.phoneCallDetailsViews.nameView.getText());
    621     }
    622 
    623     /** Asserts that the label text view contains the given text. */
    624     private void assertLabel(CallLogListItemViews views, CharSequence number,
    625             CharSequence label) {
    626         assertEquals(label == null ? View.GONE : View.VISIBLE,
    627                 views.phoneCallDetailsViews.labelView.getVisibility());
    628         if (label != null) {
    629             assertEquals(label, views.phoneCallDetailsViews.labelView.getText().toString());
    630         }
    631     }
    632 }
    633