CallLogFragmentTest.java revision e13f5e1f88b197c890b1a753dc9c8ba9a0323f46
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
17package com.android.dialer.calllog;
18
19import android.app.FragmentManager;
20import android.app.FragmentTransaction;
21import android.content.ComponentName;
22import android.content.ContentUris;
23import android.content.Context;
24import android.content.Intent;
25import android.content.res.Resources;
26import android.database.MatrixCursor;
27import android.graphics.Bitmap;
28import android.graphics.drawable.BitmapDrawable;
29import android.net.Uri;
30import android.provider.CallLog.Calls;
31import android.provider.ContactsContract.CommonDataKinds.Phone;
32import android.provider.VoicemailContract;
33import android.telephony.PhoneNumberUtils;
34import android.telephony.TelephonyManager;
35import android.test.ActivityInstrumentationTestCase2;
36import android.test.suitebuilder.annotation.LargeTest;
37import android.test.suitebuilder.annotation.MediumTest;
38import android.util.Log;
39import android.view.View;
40import android.widget.FrameLayout;
41
42import com.android.contacts.common.test.FragmentTestActivity;
43import com.android.dialer.CallDetailActivity;
44import com.android.dialer.R;
45
46import java.util.Date;
47import java.util.Formatter;
48import java.util.HashMap;
49import 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
62public 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@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@gmail.com", "sip:johndoe@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