Update call log adapter and related files for archived voicemails.

+ Populate CallLogAdapter items with different data depending on
whether it is in the voicemail archive activity or not
+ Added addVoicemailGroups method to CallLogGroupBuilder so that every
voicemail is put into an individual group
+ Voicemails grouped differently than regular calls, so added
changeCursorVoicemail in GroupingListAdapter
+Fix CallLogAdapter tests and add test for archive adapter

BUG=22797391

Change-Id: Ib8387c5b3ab8c5e39876cfaf20fde5a44295f152
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 6f96ee5..e97f8e2 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -16,7 +16,10 @@
 
 package com.android.dialer.calllog;
 
+import com.android.contacts.common.util.PermissionsUtil;
+
 import com.android.dialer.DialtactsActivity;
+import com.android.dialer.database.VoicemailArchiveContract;
 import com.google.common.annotations.VisibleForTesting;
 
 import android.content.Context;
@@ -72,6 +75,11 @@
                 VoicemailPlaybackPresenter.OnVoicemailDeletedListener,
                 ExtendedBlockingButtonRenderer.Listener {
 
+    // Types of activities the call log adapter is used for
+    public static final int ACTIVITY_TYPE_CALL_LOG = 1;
+    public static final int ACTIVITY_TYPE_ARCHIVE = 2;
+    public static final int ACTIVITY_TYPE_DIALTACTS = 3;
+
     /** Interface used to initiate a refresh of the content. */
     public interface CallFetcher {
         public void fetchCalls();
@@ -102,7 +110,7 @@
 
     protected ContactInfoCache mContactInfoCache;
 
-    private boolean mIsCallLogActivity;
+    private final int mActivityType;
 
     private static final String KEY_EXPANDED_POSITION = "expanded_position";
     private static final String KEY_EXPANDED_ROW_ID = "expanded_row_id";
@@ -172,7 +180,7 @@
             } else {
                 if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
                     CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
-                    if (!mIsCallLogActivity) {
+                    if (mActivityType == ACTIVITY_TYPE_DIALTACTS) {
                         ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
                     }
                 }
@@ -255,7 +263,7 @@
             CallFetcher callFetcher,
             ContactInfoHelper contactInfoHelper,
             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
-            boolean isCallLogActivity) {
+            int activityType) {
         super(context);
 
         mContext = context;
@@ -265,7 +273,8 @@
         if (mVoicemailPlaybackPresenter != null) {
             mVoicemailPlaybackPresenter.setOnVoicemailDeletedListener(this);
         }
-        mIsCallLogActivity = isCallLogActivity;
+
+        mActivityType = activityType;
 
         mContactInfoCache = new ContactInfoCache(
                 mContactInfoHelper, mOnContactInfoChangedListener);
@@ -375,6 +384,11 @@
     }
 
     @Override
+    public void addVoicemailGroups(Cursor cursor) {
+        mCallLogGroupBuilder.addVoicemailGroups(cursor);
+    }
+
+    @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if (viewType == VIEW_TYPE_VOICEMAIL_PROMO_CARD) {
             return createVoicemailPromoCardViewHolder(parent);
@@ -415,7 +429,7 @@
 
                     @Override
                     public void onChangeFilteredNumberUndo() {}
-                });
+                }, mActivityType == ACTIVITY_TYPE_ARCHIVE);
 
         viewHolder.callLogEntryView.setTag(viewHolder);
         viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
@@ -479,7 +493,8 @@
 
         final String number = c.getString(CallLogQuery.NUMBER);
         final String postDialDigits = PhoneNumberDisplayUtil.canShowPostDial()
-                ? c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
+                && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
+                c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
 
         final int numberPresentation = c.getInt(CallLogQuery.NUMBER_PRESENTATION);
         final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
@@ -506,17 +521,13 @@
                 mContext, number, numberPresentation, formattedNumber,
                 postDialDigits, isVoicemailNumber);
         details.accountHandle = accountHandle;
-        details.callTypes = getCallTypes(c, count);
         details.countryIso = countryIso;
         details.date = c.getLong(CallLogQuery.DATE);
         details.duration = c.getLong(CallLogQuery.DURATION);
         details.features = getCallFeatures(c, count);
         details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
         details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
-        if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
-                details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
-            details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
-        }
+        details.callTypes = getCallTypes(c, count);
 
         if (!c.isNull(CallLogQuery.DATA_USAGE)) {
             details.dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
@@ -543,9 +554,8 @@
         views.postDialDigits = details.postDialDigits;
         views.displayNumber = details.displayNumber;
         views.numberPresentation = numberPresentation;
-        views.callType = c.getInt(CallLogQuery.CALL_TYPE);
+
         views.accountHandle = accountHandle;
-        views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
         // Stash away the Ids of the calls so that we can support deleting a row in the call log.
         views.callIds = getCallIds(c, count);
         views.isBusiness = mContactInfoHelper.isBusiness(info.sourceType);
@@ -566,6 +576,21 @@
             views.dayGroupHeader.setVisibility(View.GONE);
         }
 
+        if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
+            views.callType = CallLog.Calls.VOICEMAIL_TYPE;
+            views.voicemailUri = VoicemailArchiveContract.VoicemailArchive.buildWithId(c.getInt(
+                    c.getColumnIndex(VoicemailArchiveContract.VoicemailArchive._ID)))
+                    .toString();
+
+        } else {
+            if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
+                    details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
+                details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
+            }
+            views.callType = c.getInt(CallLogQuery.CALL_TYPE);
+            views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
+        }
+
         mCallLogListItemHelper.setPhoneCallDetails(views, details);
 
         if (mCurrentlyExpandedRowId == views.rowId) {
@@ -613,7 +638,7 @@
     public Object getItem(int position) {
         return super.getItem(position - (mShowVoicemailPromoCard ? 1 : 0)
                 + ((mHiddenPosition != RecyclerView.NO_POSITION && position >= mHiddenPosition)
-                        ? 1 : 0));
+                ? 1 : 0));
     }
 
     @Override
@@ -622,7 +647,7 @@
     }
 
     protected boolean isCallLogActivity() {
-        return mIsCallLogActivity;
+        return mActivityType == ACTIVITY_TYPE_CALL_LOG;
     }
 
     /**
@@ -740,6 +765,9 @@
      * It position in the cursor is unchanged by this function.
      */
     private int[] getCallTypes(Cursor cursor, int count) {
+        if (mActivityType == ACTIVITY_TYPE_ARCHIVE) {
+            return new int[] {CallLog.Calls.VOICEMAIL_TYPE};
+        }
         int position = cursor.getPosition();
         int[] callTypes = new int[count];
         for (int index = 0; index < count; ++index) {
@@ -851,7 +879,8 @@
     private void maybeShowVoicemailPromoCard() {
         boolean showPromoCard = mPrefs.getBoolean(SHOW_VOICEMAIL_PROMO_CARD,
                 SHOW_VOICEMAIL_PROMO_CARD_DEFAULT);
-        mShowVoicemailPromoCard = (mVoicemailPlaybackPresenter != null) && showPromoCard;
+        mShowVoicemailPromoCard = mActivityType != ACTIVITY_TYPE_ARCHIVE &&
+                (mVoicemailPlaybackPresenter != null) && showPromoCard;
     }
 
     /**
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 9cd1359..07299a2 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -300,13 +300,15 @@
         mEmptyListView.setImage(R.drawable.empty_call_log);
         mEmptyListView.setActionClickedListener(this);
 
+        int activityType = mIsCallLogActivity ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG :
+                CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
         String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
         mAdapter = ObjectFactory.newCallLogAdapter(
-                getActivity(),
-                this,
-                new ContactInfoHelper(getActivity(), currentCountryIso),
-                voicemailPlaybackPresenter,
-                mIsCallLogActivity);
+                        getActivity(),
+                        this,
+                        new ContactInfoHelper(getActivity(), currentCountryIso),
+                        voicemailPlaybackPresenter,
+                        activityType);
         mRecyclerView.setAdapter(mAdapter);
         fetchCalls();
     }
diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
index 194231b..950f634 100644
--- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java
+++ b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
@@ -187,6 +187,33 @@
         mGroupCreator.addGroup(count - groupSize, groupSize);
     }
 
+    /**
+     * Group cursor entries by date, with only one entry per group. This is used for listing
+     * voicemails in the archive tab.
+     */
+    public void addVoicemailGroups(Cursor cursor) {
+        if (cursor.getCount() == 0) {
+            return;
+        }
+
+        // Clear any previous day grouping information.
+        mGroupCreator.clearDayGroups();
+
+        // Get current system time, used for calculating which day group calls belong to.
+        long currentTime = System.currentTimeMillis();
+
+        // Reset cursor to start before the first row
+        cursor.moveToPosition(-1);
+
+        // Create an individual group for each voicemail
+        while (cursor.moveToNext()) {
+            mGroupCreator.addGroup(cursor.getPosition(), 1);
+            mGroupCreator.setDayGroup(cursor.getLong(CallLogQuery.ID),
+                    getDayGroup(cursor.getLong(CallLogQuery.DATE), currentTime));
+
+        }
+    }
+
     @VisibleForTesting
     boolean equalNumbers(String number1, String number2) {
         if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index 6c25275..392672f 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -34,6 +34,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewStub;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
@@ -200,6 +201,11 @@
      */
     public boolean isBlocked;
 
+    /**
+     * Whether this is the archive tab or not.
+     */
+    public final boolean isArchiveTab;
+
     private final Context mContext;
     private final CallLogCache mCallLogCache;
     private final CallLogListItemHelper mCallLogListItemHelper;
@@ -230,7 +236,8 @@
             PhoneCallDetailsViews phoneCallDetailsViews,
             CardView callLogEntryView,
             TextView dayGroupHeader,
-            ImageView primaryActionButtonView) {
+            ImageView primaryActionButtonView,
+            boolean isArchiveTab) {
         super(rootView);
 
         mContext = context;
@@ -249,6 +256,7 @@
         this.dayGroupHeader = dayGroupHeader;
         this.primaryActionButtonView = primaryActionButtonView;
         this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon);
+        this.isArchiveTab = isArchiveTab;
         Resources resources = mContext.getResources();
         mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
 
@@ -276,7 +284,9 @@
             CallLogListItemHelper callLogListItemHelper,
             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
             FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
-            BlockNumberDialogFragment.Callback filteredNumberDialogCallback) {
+            BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
+            boolean isArchiveTab) {
+
         return new CallLogListItemViewHolder(
                 context,
                 eventListener,
@@ -292,7 +302,8 @@
                 PhoneCallDetailsViews.fromView(view),
                 (CardView) view.findViewById(R.id.call_log_row),
                 (TextView) view.findViewById(R.id.call_log_day_group_label),
-                (ImageView) view.findViewById(R.id.primary_action_button));
+                (ImageView) view.findViewById(R.id.primary_action_button),
+                isArchiveTab);
     }
 
     @Override
@@ -397,6 +408,10 @@
 
             voicemailPlaybackView = (VoicemailPlaybackLayout) actionsView
                     .findViewById(R.id.voicemail_playback_layout);
+            if (isArchiveTab) {
+                voicemailPlaybackView.hideArchiveButton();
+            }
+
 
             callButtonView = actionsView.findViewById(R.id.call_action);
             callButtonView.setOnClickListener(this);
@@ -509,8 +524,10 @@
             mVoicemailPlaybackPresenter.setPlaybackView(
                     voicemailPlaybackView, uri, mVoicemailPrimaryActionButtonClicked);
             mVoicemailPrimaryActionButtonClicked = false;
-
-            CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
+            // Only mark voicemail as read when not in archive tab
+            if (!isArchiveTab) {
+                CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
+            }
         } else {
             voicemailPlaybackView.setVisibility(View.GONE);
         }
@@ -705,11 +722,12 @@
                 PhoneCallDetailsViews.createForTest(context),
                 new CardView(context),
                 new TextView(context),
-                new ImageView(context));
+                new ImageView(context),
+                false);
         viewHolder.detailsButtonView = new TextView(context);
         viewHolder.actionsView = new View(context);
         viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context);
-
+        viewHolder.workIconView = new ImageButton(context);
         return viewHolder;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 3b493cf..d1591e1 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -38,6 +38,7 @@
 import com.android.contacts.common.compat.SdkVersionOverride;
 import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
 import com.android.contacts.common.util.PermissionsUtil;
+import com.android.dialer.database.VoicemailArchiveContract;
 import com.android.dialer.util.AppCompatConstants;
 import com.android.dialer.util.TelecomUtil;
 import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
@@ -64,6 +65,8 @@
     private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
     /** The token for the query to fetch the number of missed calls. */
     private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
+    /** The oken for the query to fetch the archived voicemails. */
+    private static final int QUERY_VOICEMAIL_ARCHIVE = 60;
 
     private final int mLogLimit;
 
@@ -127,6 +130,17 @@
     }
 
     /**
+     * Fetch all the voicemails in the voicemail archive.
+     */
+    public void fetchVoicemailArchive() {
+        startQuery(QUERY_VOICEMAIL_ARCHIVE, null,
+                VoicemailArchiveContract.VoicemailArchive.CONTENT_URI,
+                null, VoicemailArchiveContract.VoicemailArchive.ARCHIVED + " = 1", null,
+                VoicemailArchiveContract.VoicemailArchive.DATE + " DESC");
+    }
+
+
+    /**
      * Fetches the list of calls from the call log for a given type.
      * This call ignores the new or old state.
      * <p>
@@ -253,7 +267,7 @@
             return;
         }
         try {
-            if (token == QUERY_CALLLOG_TOKEN) {
+            if (token == QUERY_CALLLOG_TOKEN || token == QUERY_VOICEMAIL_ARCHIVE) {
                 if (updateAdapterData(cursor)) {
                     cursor = null;
                 }
diff --git a/src/com/android/dialer/calllog/GroupingListAdapter.java b/src/com/android/dialer/calllog/GroupingListAdapter.java
index 70190df..0d06298 100644
--- a/src/com/android/dialer/calllog/GroupingListAdapter.java
+++ b/src/com/android/dialer/calllog/GroupingListAdapter.java
@@ -73,9 +73,19 @@
      */
     protected abstract void addGroups(Cursor cursor);
 
+    protected abstract void addVoicemailGroups(Cursor cursor);
+
     protected abstract void onContentChanged();
 
     public void changeCursor(Cursor cursor) {
+        changeCursor(cursor, false);
+    }
+
+    public void changeCursorVoicemail(Cursor cursor) {
+        changeCursor(cursor, true);
+    }
+
+    public void changeCursor(Cursor cursor, boolean voicemail) {
         if (cursor == mCursor) {
             return;
         }
@@ -91,7 +101,11 @@
         mCursor = cursor;
 
         if (cursor != null) {
-            addGroups(mCursor);
+            if (voicemail) {
+                addVoicemailGroups(mCursor);
+            } else {
+                addGroups(mCursor);
+            }
 
             // Calculate the item count by subtracting group child counts from the cursor count.
             mItemCount = mGroupMetadata.size();
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index 31255a2..4568c1b 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -62,13 +62,13 @@
             CallFetcher callFetcher,
             ContactInfoHelper contactInfoHelper,
             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
-            boolean isCallLogActivity) {
+            int activityType) {
         return new CallLogAdapter(
                 context,
                 callFetcher,
                 contactInfoHelper,
                 voicemailPlaybackPresenter,
-                isCallLogActivity);
+                activityType);
     }
 
     public static Logger getLoggerInstance() {
diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
index 80dfe35..e801311 100644
--- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
@@ -31,6 +31,7 @@
 
 import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.dialer.contactinfo.ContactInfoCache;
+import com.android.dialer.database.VoicemailArchiveContract;
 import com.android.dialer.util.AppCompatConstants;
 import com.android.dialer.util.TestConstants;
 import com.google.common.collect.Lists;
@@ -49,6 +50,7 @@
 public class CallLogAdapterTest extends AndroidTestCase {
     private static final String EMPTY_STRING = "";
     private static final int NO_VALUE_SET = -1;
+    private static final int ARCHIVE_TYPE = -2;
 
     private static final String TEST_CACHED_NAME_PRIMARY = "Cached Name";
     private static final String TEST_CACHED_NAME_ALTERNATIVE = "Name Cached";
@@ -74,12 +76,11 @@
 
     private View mView;
     private CallLogListItemViewHolder mViewHolder;
-    private Random mRandom;
+    private final Random mRandom = new Random();
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mRandom = new Random();
 
         // Use a call fetcher that does not do anything.
         CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() {
@@ -98,7 +99,8 @@
                     }
                 };
 
-        mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper);
+        mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper,
+                CallLogAdapter.ACTIVITY_TYPE_DIALTACTS);
 
         // The cursor used in the tests to store the entries to display.
         mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
@@ -108,13 +110,6 @@
         mViewHolder = CallLogListItemViewHolder.createForTest(getContext());
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        mAdapter = null;
-        mCursor = null;
-        super.tearDown();
-    }
-
     @MediumTest
     public void testBindView_NumberOnlyNoCache() {
         createCallLogEntry();
@@ -547,6 +542,19 @@
         assertEquals(TEST_NUMBER_3, mViewHolder.number);
     }
 
+    public void testVoicemailArchive() {
+        setUpArchiveAdapter();
+        createVoicemailArchiveCallLogEntry();
+
+        mAdapter.changeCursorVoicemail(mCursor);
+        mAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertEquals(Uri.parse(mViewHolder.voicemailUri),
+                ContentUris.withAppendedId(
+                        VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, 0));
+        assertNull(mViewHolder.primaryActionButtonView.getTag());
+    }
+
     private void createCallLogEntry() {
         createCallLogEntry(TEST_NUMBER);
     }
@@ -575,6 +583,10 @@
         createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, Calls.VOICEMAIL_TYPE);
     }
 
+    private void createVoicemailArchiveCallLogEntry() {
+        createCallLogEntry(TEST_NUMBER, EMPTY_STRING, NO_VALUE_SET, ARCHIVE_TYPE);
+    }
+
     private void createCallLogEntry(String number, String postDialDigits, int presentation, int type) {
         Object[] values = getValues(number, postDialDigits, presentation, type);
         mCursor.addRow(values);
@@ -674,6 +686,10 @@
             values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId(
                     VoicemailContract.Voicemails.CONTENT_URI, mCursor.getCount());
         }
+        if (type == ARCHIVE_TYPE) {
+            values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId(
+                    VoicemailArchiveContract.VoicemailArchive.CONTENT_URI, mCursor.getCount());
+        }
 
         return values;
     }
@@ -739,11 +755,34 @@
         return Phone.getTypeLabel(getContext().getResources(), phoneType, "");
     }
 
+    private void setUpArchiveAdapter() {
+        // Use a call fetcher that does not do anything.
+        CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() {
+            @Override
+            public void fetchCalls() {}
+        };
+
+        ContactInfoHelper fakeContactInfoHelper =
+                new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) {
+                    @Override
+                    public ContactInfo lookupNumber(String number, String countryIso) {
+                        ContactInfo info = new ContactInfo();
+                        info.number = number;
+                        info.formattedNumber = number;
+                        return info;
+                    }
+                };
+
+        mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper,
+                CallLogAdapter.ACTIVITY_TYPE_ARCHIVE);
+    }
+
     /// Subclass of {@link CallLogAdapter} used in tests to intercept certain calls.
     private static final class TestCallLogAdapter extends CallLogAdapter {
         public TestCallLogAdapter(Context context, CallFetcher callFetcher,
-                ContactInfoHelper contactInfoHelper) {
-            super(context, callFetcher, contactInfoHelper, null, false);
+                ContactInfoHelper contactInfoHelper, int mActivity) {
+            super(context, callFetcher, contactInfoHelper, null,
+                    mActivity);
             mContactInfoCache = new TestContactInfoCache(
                     contactInfoHelper, mOnContactInfoChangedListener);
         }
diff --git a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
index 04463c2..c31c38e 100644
--- a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
@@ -129,6 +129,18 @@
                 AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
     }
 
+    public void testGrouping_VoicemailArchive() {
+        // Does not group with other types of calls, include voicemail themselves.
+        assertVoicemailsAreNotGrouped(
+                AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_MISSED_TYPE);
+        assertVoicemailsAreNotGrouped(
+                AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_VOICEMAIL_TYPE);
+        assertVoicemailsAreNotGrouped(
+                AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_INCOMING_TYPE);
+        assertVoicemailsAreNotGrouped(
+                AppCompatConstants.CALLS_VOICEMAIL_TYPE, AppCompatConstants.CALLS_OUTGOING_TYPE);
+    }
+
     public void testGrouping_Missed() {
         // Groups with one or more missed calls.
         assertCallsAreGrouped(
@@ -198,6 +210,21 @@
 
     }
 
+    public void testAddGroups_Separate() {
+        addMultipleCallLogEntries(TEST_NUMBER1,
+                AppCompatConstants.CALLS_VOICEMAIL_TYPE,    // Group 1: 0
+                AppCompatConstants.CALLS_INCOMING_TYPE,     // Group 2: 1
+                AppCompatConstants.CALLS_OUTGOING_TYPE,     // Group 3: 2
+                AppCompatConstants.CALLS_MISSED_TYPE);      // Group 4: 3
+        mBuilder.addVoicemailGroups(mCursor);
+
+        assertEquals(4, mFakeGroupCreator.groups.size());
+        assertGroupIs(0, 1, mFakeGroupCreator.groups.get(0));
+        assertGroupIs(1, 1, mFakeGroupCreator.groups.get(1));
+        assertGroupIs(2, 1, mFakeGroupCreator.groups.get(2));
+        assertGroupIs(3, 1, mFakeGroupCreator.groups.get(3));
+    }
+
     public void testAddGroups_Mixed() {
         addMultipleCallLogEntries(TEST_NUMBER1,
                 AppCompatConstants.CALLS_VOICEMAIL_TYPE,   // Group 1: 0
@@ -326,6 +353,15 @@
         assertEquals(types.length, mFakeGroupCreator.groups.size());
     }
 
+    /** Asserts that voicemails are not grouped together with other types at all. */
+    private void assertVoicemailsAreNotGrouped(int... types) {
+        createCursor();
+        clearFakeGroupCreator();
+        addMultipleCallLogEntries(TEST_NUMBER1, types);
+        mBuilder.addVoicemailGroups(mCursor);
+        assertEquals(types.length, mFakeGroupCreator.groups.size());
+    }
+
     /** Adds a set of calls with the given types, all from the same number, in the old section. */
     private void addMultipleCallLogEntries(String number, int... types) {
         for (int type : types) {
diff --git a/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java b/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java
index 4d51f72..4d8cb9c 100644
--- a/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java
+++ b/tests/src/com/android/dialer/calllog/GroupingListAdapterTests.java
@@ -70,6 +70,11 @@
         }
 
         @Override
+        protected void addVoicemailGroups(Cursor c) {
+            // Do nothing.
+        }
+
+        @Override
         public void onContentChanged() {
             // Do nothing.
         }