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