Call History Affordances Swap

1. Swapped the intents for the primary view
2. Changed the icon for the secondary action button
3. Added a vertical divider line to separate the secondary action button from the primary view
4. Cleaned up the accessibility text

Change-Id: Ifd4ceff0d67b1587c4378e29be7344de50057a7d
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 1d368f6..15d7a92 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -114,18 +114,38 @@
                     />
                 </LinearLayout>
             </LinearLayout>
-            <ImageButton
-                android:id="@+id/secondary_action_icon"
-                android:layout_width="@dimen/call_log_call_action_width"
+            <!-- Linear layout to house a vertical separator line and the
+                 secondary action button.  Used as a convenience to hide both
+                 the separator and action button at the same time. -->
+            <LinearLayout
+                android:id="@+id/secondary_action_view"
+                android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:paddingStart="@dimen/call_log_inner_margin"
-                android:paddingTop="@dimen/call_log_inner_margin"
-                android:paddingBottom="@dimen/call_log_inner_margin"
-                android:paddingEnd="@dimen/call_log_inner_margin"
-                android:scaleType="center"
-                android:background="?android:attr/selectableItemBackground"
-                android:nextFocusLeft="@id/primary_action_view"
-            />
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                >
+                <!-- Thin vertical divider to visually separate the secondary action button -->
+                <View
+                    android:id="@+id/vertical_divider"
+                    android:layout_width="@dimen/call_log_list_item_vertical_divider_width"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="@dimen/call_log_list_item_vertical_divider_margin"
+                    android:layout_marginBottom="@dimen/call_log_list_item_vertical_divider_margin"
+                    android:background="?android:attr/dividerVertical"/>
+                <!-- The secondary action button; either play voicemail or call details. -->
+                <ImageButton
+                    android:id="@+id/secondary_action_icon"
+                    android:layout_width="@dimen/call_log_call_action_width"
+                    android:layout_height="match_parent"
+                    android:paddingStart="@dimen/call_log_inner_margin"
+                    android:paddingTop="@dimen/call_log_inner_margin"
+                    android:paddingBottom="@dimen/call_log_inner_margin"
+                    android:paddingEnd="@dimen/call_log_inner_margin"
+                    android:scaleType="center"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:nextFocusLeft="@id/primary_action_view"
+                />
+            </LinearLayout>
         </LinearLayout>
 
     <TextView
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1743952..e84b18a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -43,6 +43,13 @@
     <dimen name="call_log_list_contact_photo_size">64dip</dimen>
     <dimen name="call_detail_contact_name_margin">24dip</dimen>
     <dimen name="call_detail_button_spacing">2dip</dimen>
+    <!-- Defines the vertical margin for the vertical separator between
+         the main area of a call log entry and the secondary action button. -->
+    <dimen name="call_log_list_item_vertical_divider_margin">8dp</dimen>
+
+    <!-- Defines the width of the vertical separator between
+         the main area of a call log entry and the secondary action button. -->
+    <dimen name="call_log_list_item_vertical_divider_width">1dp</dimen>
 
     <!-- Layout weight values for dialpad screen. These layouts will be used in one
          LinearLayout (dialpad_fragment.xml), configuring dialpad screen's vertical
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b28c8ea..c8aa37f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -441,6 +441,86 @@
     -->
     <string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
 
+    <!-- String describing the button to access the contact details for a name or number.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
+
+    <!-- String describing the button to access call details in the call log.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_details">Call details</string>
+
+    <!-- String indicating a call log entry has an associated voicemail.
+
+    Note: AccessibilityServices use this attribute to announce what the view represents.
+          This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_new_voicemail">New voicemail.</string>
+
+    <!-- String indicating the number of calls to/from a caller in the call log.
+
+    Note: AccessibilityServices use this attribute to announce what the view represents.
+          This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string>
+
+
+    <!-- Call history description for a missed call from a caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_return_missed_call">Return missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+    <!-- Call history description for an answered call for a caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_return_answered_call">Return answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+    <!-- Call history description for a missed call from an unknown caller.
+         Drops the "return" part of description_return_missed_call since it is not
+         possible to actually call an unknown number.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_unknown_missed_call">Missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+    <!-- Call history description for an answered call from an unknown caller.
+        Drops the "return" part of description_return_answered_call since it is not
+         possible to actually call an unknown number.
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_unknown_answered_call">Answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+    <!-- String describing an outgoing call entry in the call log.  Used to indicate that
+         a call will be made to the specified caller.  Used when there are multiple calls to/from
+         the caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_last_multiple">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>.  Last called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
+    <!-- String describing an outgoing call entry in the call log.  Used to indicate that
+         a call will be made to the specified caller.  Used when there is only a single call
+         related to/from the caller.
+
+        Note: AccessibilityServices use this attribute to announce what the view represents.
+              This is especially valuable for views without textual representation like ImageView.
+    -->
+    <string name="description_call_last">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>.  Called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
 
     <!-- String describing the button to SMS a number or contact.
 
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java
index 2a4a142..4424fcb 100644
--- a/src/com/android/dialer/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/PhoneCallDetailsHelper.java
@@ -91,26 +91,13 @@
                 isHighlighted ? mCallTypeHelper.getHighlightedColor(details.callTypes[0]) : null;
 
         // The date of this call, relative to the current time.
-        CharSequence dateText =
-            DateUtils.getRelativeTimeSpanString(details.date,
-                    getCurrentTimeMillis(),
-                    DateUtils.MINUTE_IN_MILLIS,
-                    DateUtils.FORMAT_ABBREV_RELATIVE);
+        CharSequence dateText = getCallDate(details);
 
         // Set the call count and date.
         setCallCountAndDate(views, callCount, dateText, highlightColor);
 
-        CharSequence numberFormattedLabel = null;
-        // Only show a label if the number is shown and it is not a SIP address.
-        if (!TextUtils.isEmpty(details.number)
-                && !PhoneNumberHelper.isUriNumber(details.number.toString())) {
-            if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
-                numberFormattedLabel = details.geocode;
-            } else {
-                numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
-                        details.numberLabel);
-            }
-        }
+        // Get type of call (ie mobile, home, etc) if known, or the caller's
+        CharSequence numberFormattedLabel = getCallTypeOrLocation(details);
 
         final CharSequence nameText;
         final CharSequence numberText;
@@ -141,6 +128,41 @@
         views.labelView.setVisibility(TextUtils.isEmpty(labelText) ? View.GONE : View.VISIBLE);
     }
 
+    /**
+     * For a call, if there is an associated contact for the caller, return the known call type
+     * (e.g. mobile, home, work).  If there is no associated contact, attempt to use the caller's
+     * location if known.
+     * @param details Call details to use.
+     * @return Type of call (mobile/home) if known, or the location of the caller (if known).
+     */
+    public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
+        CharSequence numberFormattedLabel = null;
+        // Only show a label if the number is shown and it is not a SIP address.
+        if (!TextUtils.isEmpty(details.number)
+                && !PhoneNumberHelper.isUriNumber(details.number.toString())) {
+            if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
+                numberFormattedLabel = details.geocode;
+            } else {
+                numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
+                        details.numberLabel);
+            }
+        }
+        return numberFormattedLabel;
+    }
+
+    /**
+     * Get the call date/time of the call, relative to the current time.
+     * e.g. 3 minutes ago
+     * @param details Call details to use.
+     * @return String representing when the call occurred.
+     */
+    public CharSequence getCallDate(PhoneCallDetails details) {
+        return DateUtils.getRelativeTimeSpanString(details.date,
+                getCurrentTimeMillis(),
+                DateUtils.MINUTE_IN_MILLIS,
+                DateUtils.FORMAT_ABBREV_RELATIVE);
+    }
+
     /** Sets the text of the header view for the details page of a phone call. */
     public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
         final CharSequence nameText;
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index a668196..32699e6 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -183,9 +183,11 @@
     /** Can be set to true by tests to disable processing of requests. */
     private volatile boolean mRequestProcessingDisabled = false;
 
-    /** True if CallLogAdapter is created from the PhoneFavoriteFragment, where the primary
-     * action should be set to call a number instead of opening the detail page. */
-    private boolean mUseCallAsPrimaryAction = false;
+    /**
+     * Whether to show the secondary action button used to play voicemail or show call details.
+     * True if created from a CallLogFragment.
+     * False if created from the PhoneFavoriteFragment. */
+    private boolean mShowSecondaryActionButton = true;
 
     private boolean mIsCallLog = true;
     private int mNumMissedCalls = 0;
@@ -246,14 +248,14 @@
     };
 
     public CallLogAdapter(Context context, CallFetcher callFetcher,
-            ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+            ContactInfoHelper contactInfoHelper, boolean showSecondaryActionButton,
             boolean isCallLog) {
         super(context);
 
         mContext = context;
         mCallFetcher = callFetcher;
         mContactInfoHelper = contactInfoHelper;
-        mUseCallAsPrimaryAction = useCallAsPrimaryAction;
+        mShowSecondaryActionButton = showSecondaryActionButton;
         mIsCallLog = isCallLog;
 
         mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
@@ -508,7 +510,7 @@
         // Get the views to bind to.
         CallLogListItemViews views = CallLogListItemViews.fromView(view);
         views.primaryActionView.setOnClickListener(mActionListener);
-        views.secondaryActionView.setOnClickListener(mActionListener);
+        views.secondaryActionButtonView.setOnClickListener(mActionListener);
         view.setTag(views);
     }
 
@@ -535,31 +537,30 @@
 
         final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c);
 
-        if (!mUseCallAsPrimaryAction) {
-            // Sets the primary action to open call detail page.
-            views.primaryActionView.setTag(
-                    IntentProvider.getCallDetailIntentProvider(
-                            getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
-        } else if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
+        // Primary action is always to call, if possible.
+        if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
             // Sets the primary action to call the number.
             views.primaryActionView.setTag(IntentProvider.getReturnCallIntentProvider(number));
         } else {
             views.primaryActionView.setTag(null);
         }
 
-        // Store away the voicemail information so we can play it directly.
-        if (callType == Calls.VOICEMAIL_TYPE) {
-            String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
-            final long rowId = c.getLong(CallLogQuery.ID);
-            views.secondaryActionView.setTag(
-                    IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
-        } else if (!TextUtils.isEmpty(number)) {
-            // Store away the number so we can call it directly if you click on the call icon.
-            views.secondaryActionView.setTag(
-                    IntentProvider.getReturnCallIntentProvider(number));
+        if ( mShowSecondaryActionButton ) {
+            // Store away the voicemail information so we can play it directly.
+            if (callType == Calls.VOICEMAIL_TYPE) {
+                String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
+                final long rowId = c.getLong(CallLogQuery.ID);
+                views.secondaryActionButtonView.setTag(
+                        IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
+            } else {
+                // Store the call details information.
+                views.secondaryActionButtonView.setTag(
+                        IntentProvider.getCallDetailIntentProvider(
+                                getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
+            }
         } else {
             // No action enabled.
-            views.secondaryActionView.setTag(null);
+            views.secondaryActionButtonView.setTag(null);
         }
 
         // Lookup contacts with this number
@@ -624,7 +625,7 @@
         // New items also use the highlighted version of the text.
         final boolean isHighlighted = isNew;
         mCallLogViewsHelper.setPhoneCallDetails(views, details, isHighlighted,
-                mUseCallAsPrimaryAction);
+                mShowSecondaryActionButton);
 
         if (photoId == 0 && photoUri != null) {
             setPhoto(views, photoUri, lookupUri);
@@ -632,9 +633,6 @@
             setPhoto(views, photoId, lookupUri);
         }
 
-        views.quickContactView.setContentDescription(views.phoneCallDetailsViews.nameView.
-                getText());
-
         // Listen for the first draw
         if (mViewTreeObserver == null) {
             mViewTreeObserver = view.getViewTreeObserver();
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 826abe1..9526f39 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -235,7 +235,7 @@
         updateEmptyMessage(mCallTypeFilter);
         String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
         mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, new ContactInfoHelper(
-                getActivity(), currentCountryIso), false, true);
+                getActivity(), currentCountryIso), true, true);
         setListAdapter(mAdapter);
         getListView().setItemsCanFocus(true);
     }
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index a38ef01..a85cd01 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -17,6 +17,7 @@
 package com.android.dialer.calllog;
 
 import android.content.res.Resources;
+import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.text.TextUtils;
 import android.view.View;
@@ -55,37 +56,201 @@
      * @param views the views to populate
      * @param details the details of a phone call needed to fill in the data
      * @param isHighlighted whether to use the highlight text for the call
+     * @param showSecondaryActionButton whether to show the secondary action button or not
      */
     public void setPhoneCallDetails(CallLogListItemViews views, PhoneCallDetails details,
-            boolean isHighlighted, boolean useCallAsPrimaryAction) {
+            boolean isHighlighted, boolean showSecondaryActionButton) {
         mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details,
                 isHighlighted);
-        boolean canCall = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
-                details.numberPresentation);
         boolean canPlay = details.callTypes[0] == Calls.VOICEMAIL_TYPE;
 
-        if (canPlay) {
-            // Playback action takes preference.
-            configurePlaySecondaryAction(views, isHighlighted);
-        } else if (canCall && !useCallAsPrimaryAction) {
-            // Call is the secondary action.
-            configureCallSecondaryAction(views, details);
+        // Set the accessibility text for the contact badge
+        views.quickContactView.setContentDescription(getContactBadgeDescription(details));
+
+        // Set the primary action accessibility description
+        views.primaryActionView.setContentDescription(getCallDescription(details));
+
+        // If secondary action is visible, either show voicemail playback icon, or
+        // show the "clock" icon corresponding to the call details screen.
+        if (showSecondaryActionButton) {
+            if (canPlay) {
+                // Playback action takes preference.
+                configurePlaySecondaryAction(views, isHighlighted);
+            } else {
+                // Call details is the secondary action.
+                configureCallDetailsSecondaryAction(views, details);
+            }
         } else {
-            // No action available.
+            // No secondary action is to be shown (ie this is likely a PhoneFavoriteFragment)
             views.secondaryActionView.setVisibility(View.GONE);
         }
     }
 
-    /** Sets the secondary action to correspond to the call button. */
-    private void configureCallSecondaryAction(CallLogListItemViews views,
+    /**
+     * Sets the secondary action to invoke call details.
+     *
+     * @param views   the views to populate
+     * @param details the details of a phone call needed to fill in the call details data
+     */
+    private void configureCallDetailsSecondaryAction(CallLogListItemViews views,
             PhoneCallDetails details) {
         views.secondaryActionView.setVisibility(View.VISIBLE);
-        views.secondaryActionView.setImageResource(R.drawable.ic_phone_dk);
-        views.secondaryActionView.setContentDescription(getCallActionDescription(details));
+        // Use the small dark grey clock icon.
+        views.secondaryActionButtonView.setImageResource(R.drawable.ic_menu_history_dk);
+        views.secondaryActionButtonView.setContentDescription(
+                mResources.getString(R.string.description_call_details));
     }
 
-    /** Returns the description used by the call action for this phone call. */
-    private CharSequence getCallActionDescription(PhoneCallDetails details) {
+    /**
+     * Returns the accessibility description for the contact badge for a call log entry.
+     *
+     * @param details Details of call.
+     * @return Accessibility description.
+     */
+    private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
+        return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
+    }
+
+    /**
+     * Returns the accessibility description of the "return call/call" action for a call log
+     * entry.
+     * Accessibility text is a combination of:
+     * {Voicemail Prefix}. {Number of Calls}. {Caller information}.
+     * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
+     *
+     * If more than one call for the caller, {Number of Calls} is:
+     * "{number of calls} calls.", otherwise "".
+     *
+     * The {Caller Information} references the most recent call associated with the caller.
+     * For incoming calls:
+     * If missed call:  Return missed call from {Name/Number} {Call Type} {Call Time}.
+     * If answered call: Return answered call from {Name/Number} {Call Type} {Call Time}.
+     *
+     * For unknown callers, drop the "Return" part, since the call can't be returned:
+     * If answered unknown: Answered call from {Name/Number} {Call Time}.
+     * If missed unknown: Missed call from {Name/Number} {Call Time}.
+     *
+     * For outgoing calls:
+     * If outgoing:  Call {Name/Number] {Call Type}.  {Last} called {Call Time}.
+     * Where {Last} is dropped if the number of calls for the caller is 1.
+     *
+     * Where:
+     * {Name/Number} is the name or number of the caller (as shown in call log).
+     * {Call type} is the contact phone number type (eg mobile) or location.
+     * {Call Time} is the time since the last call for the contact occurred.
+     *
+     * Examples:
+     * 3 calls.  New Voicemail.  Return missed call from Joe Smith mobile 2 hours ago.
+     * 2 calls.  Call John Doe mobile.  Last called 1 hour ago.
+     * @param details Details of call.
+     * @return Return call action description.
+     */
+    public CharSequence getCallDescription(PhoneCallDetails details) {
+        int lastCallType = getLastCallType(details.callTypes);
+        boolean isVoiceMail = lastCallType == Calls.VOICEMAIL_TYPE;
+
+        // Get the name or number of the caller.
+        final CharSequence nameOrNumber = getNameOrNumber(details);
+
+        // Get the call type or location of the caller; null if not applicable
+        final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
+
+        // Get the time/date of the call
+        final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
+
+        StringBuilder callDescription = new StringBuilder();
+
+        // Prepend the voicemail indication.
+        if (isVoiceMail) {
+            callDescription.append(mResources.getString(R.string.description_new_voicemail));
+        }
+
+        // Add number of calls if more than one.
+        if (details.callTypes.length > 1) {
+            callDescription.append(mResources.getString(R.string.description_num_calls,
+                    details.callTypes.length));
+        }
+
+        int stringID = getCallDescriptionStringID(details);
+
+        // Use chosen string resource to build up the message.
+        callDescription.append(mResources.getString(stringID,
+                nameOrNumber,
+                // If no type or location can be determined, sub in empty string.
+                typeOrLocation == null ? "" : typeOrLocation,
+                timeOfCall));
+
+        return callDescription;
+    }
+
+    /**
+     * Determine the appropriate string ID to describe a call for accessibility purposes.
+     *
+     * @param details Call details.
+     * @return String resource ID to use.
+     */
+    public int getCallDescriptionStringID(PhoneCallDetails details) {
+        int lastCallType = getLastCallType(details.callTypes);
+        boolean isNumberCallable = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
+                details.numberPresentation);
+
+        // Default string to use is "call XYZ..." just in case we manage to fall through.
+        int stringID = R.string.description_call_last_multiple;
+
+        if (!isNumberCallable) {
+            // Number isn't callable; this is an incoming call from an unknown caller.
+            // An uncallable outgoing call wouldn't be in the call log.
+
+            // Voicemail and missed calls are both considered missed.
+            if (lastCallType == Calls.VOICEMAIL_TYPE ||
+                    lastCallType == Calls.MISSED_TYPE) {
+                stringID = R.string.description_unknown_missed_call;
+            } else if (lastCallType == Calls.INCOMING_TYPE) {
+                stringID = R.string.description_unknown_answered_call;
+            }
+        } else {
+            // Known caller, so callable.
+
+            // Missed call (ie voicemail or missed)
+            if (lastCallType == Calls.VOICEMAIL_TYPE ||
+                    lastCallType == Calls.MISSED_TYPE) {
+                stringID = R.string.description_return_missed_call;
+            } else if (lastCallType == Calls.INCOMING_TYPE) {
+                // Incoming answered.
+                stringID = R.string.description_return_answered_call;
+            } else {
+                // Outgoing call.
+
+                // If we have a history of multiple calls
+                if (details.callTypes.length > 1) {
+                    stringID = R.string.description_call_last_multiple;
+                } else {
+                    stringID = R.string.description_call_last;
+                }
+            }
+        }
+        return stringID;
+    }
+
+    /**
+     * Determine the call type for the most recent call.
+     * @param callTypes Call types to check.
+     * @return Call type.
+     */
+    private int getLastCallType(int[] callTypes) {
+        if (callTypes.length > 0) {
+            return callTypes[0];
+        } else {
+            return Calls.MISSED_TYPE;
+        }
+    }
+
+    /**
+     * Return the name or number of the caller specified by the details.
+     * @param details Call details
+     * @return the name (if known) of the caller, otherwise the formatted number.
+     */
+    private CharSequence getNameOrNumber(PhoneCallDetails details) {
         final CharSequence recipient;
         if (!TextUtils.isEmpty(details.name)) {
             recipient = details.name;
@@ -93,15 +258,15 @@
             recipient = mPhoneNumberHelper.getDisplayNumber(
                     details.number, details.numberPresentation, details.formattedNumber);
         }
-        return mResources.getString(R.string.description_call, recipient);
+        return recipient;
     }
 
     /** Sets the secondary action to correspond to the play button. */
     private void configurePlaySecondaryAction(CallLogListItemViews views, boolean isHighlighted) {
         views.secondaryActionView.setVisibility(View.VISIBLE);
-        views.secondaryActionView.setImageResource(
+        views.secondaryActionButtonView.setImageResource(
                 isHighlighted ? R.drawable.ic_play_active_holo_dark : R.drawable.ic_play_holo_light);
-        views.secondaryActionView.setContentDescription(
+        views.secondaryActionButtonView.setContentDescription(
                 mResources.getString(R.string.description_call_log_play_button));
     }
 }
diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java
index ed6218f..a378956 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViews.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViews.java
@@ -34,19 +34,25 @@
     public final QuickContactBadge quickContactView;
     /** The primary action view of the entry. */
     public final View primaryActionView;
+    /** The secondary action view, which includes both the vertical divider line and
+     *  the action button itself.  Used so that the button and divider line can be
+     *  made visible/hidden as a whole. */
+    public final View secondaryActionView;
     /** The secondary action button on the entry. */
-    public final ImageView secondaryActionView;
+    public final ImageView secondaryActionButtonView;
     /** The details of the phone call. */
     public final PhoneCallDetailsViews phoneCallDetailsViews;
     /** The text of the header of a section. */
     public final TextView listHeaderTextView;
 
     private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView,
-            ImageView secondaryActionView, PhoneCallDetailsViews phoneCallDetailsViews,
+            View secondaryActionView, ImageView secondaryActionButtonView,
+            PhoneCallDetailsViews phoneCallDetailsViews,
             TextView listHeaderTextView) {
         this.quickContactView = quickContactView;
         this.primaryActionView = primaryActionView;
         this.secondaryActionView = secondaryActionView;
+        this.secondaryActionButtonView = secondaryActionButtonView;
         this.phoneCallDetailsViews = phoneCallDetailsViews;
         this.listHeaderTextView = listHeaderTextView;
     }
@@ -55,6 +61,7 @@
         return new CallLogListItemViews(
                 (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
                 view.findViewById(R.id.primary_action_view),
+                view.findViewById(R.id.secondary_action_view),
                 (ImageView) view.findViewById(R.id.secondary_action_icon),
                 PhoneCallDetailsViews.fromView(view),
                 (TextView) view.findViewById(R.id.call_log_header));
@@ -65,6 +72,7 @@
         return new CallLogListItemViews(
                 new QuickContactBadge(context),
                 new View(context),
+                new View(context),
                 new ImageView(context),
                 PhoneCallDetailsViews.createForTest(context),
                 new TextView(context));
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 81d9bfd..2791f15 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -253,7 +253,7 @@
                 this, 1);
         final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
         mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this,
-                new ContactInfoHelper(getActivity(), currentCountryIso), true, false);
+                new ContactInfoHelper(getActivity(), currentCountryIso), false, false);
         setHasOptionsMenu(true);
     }
 
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index c43dffc..be91e33 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -34,10 +34,20 @@
         return null;
     }
 
+    /**
+     * Create a new instance of the call log adapter.
+     * @param context The context to use.
+     * @param callFetcher Instance of call fetcher to use.
+     * @param contactInfoHelper Instance of contact info helper class to use.
+     * @param hideSecondaryAction If true, secondary action will be hidden (ie call details
+     *                            or play voicemail).
+     * @param isCallLog Is this call log adapter being used on the call log?
+     * @return Instance of CallLogAdapter.
+     */
     public static CallLogAdapter newCallLogAdapter(Context context, CallFetcher callFetcher,
-            ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+            ContactInfoHelper contactInfoHelper, boolean hideSecondaryAction,
             boolean isCallLog) {
-        return new CallLogAdapter(context, callFetcher, contactInfoHelper, useCallAsPrimaryAction,
+        return new CallLogAdapter(context, callFetcher, contactInfoHelper, hideSecondaryAction,
                 isCallLog);
     }
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 3a714e3..94aa3aa 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
 
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
     <uses-permission android:name="android.permission.READ_SYNC_STATS" />
diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
index d0a8725..25f5379 100644
--- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
@@ -118,7 +118,7 @@
         FragmentManager fragmentManager = mActivity.getFragmentManager();
         FragmentTransaction transaction = fragmentManager.beginTransaction();
         transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment);
-        transaction.commit();
+        transaction.commitAllowingStateLoss();
         // Wait for the fragment to be loaded.
         getInstrumentation().waitForIdleSync();
 
@@ -323,7 +323,12 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+
+        // The primaryActionView tag is set in the
+        // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method.  If it is possible
+        // to place a call to the phone number, a call intent will have been created for the
+        // primaryActionView.
+        IntentProvider intentProvider = (IntentProvider) views.primaryActionView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts a call.
         assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction());
@@ -339,7 +344,7 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+        IntentProvider intentProvider = (IntentProvider) views.secondaryActionButtonView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts the call detail activity.
         assertEquals(new ComponentName(mActivity, CallDetailActivity.class),
@@ -365,8 +370,9 @@
     // HELPERS to check conditions on the DB/views
     //
     /**
-     * Go over all the views in the list and check that the Call
-     * icon's visibility matches the nature of the number.
+     * Go over the views in the list and check to ensure that
+     * callable numbers have an associated call intent, where numbers
+     * which are not callable have a null intent.
      */
     private void checkCallStatus() {
         for (int i = 0; i < mList.length; i++) {
@@ -377,9 +383,17 @@
             int presentation = getPhoneNumberPresentationForListEntry(i);
             if (presentation == Calls.PRESENTATION_RESTRICTED ||
                     presentation == Calls.PRESENTATION_UNKNOWN) {
-                assertFalse(View.VISIBLE == mItem.secondaryActionView.getVisibility());
+                //If number is not callable, the primary action view should have a null tag.
+                assertNull(mItem.primaryActionView.getTag());
             } else {
-                assertEquals(View.VISIBLE, mItem.secondaryActionView.getVisibility());
+                //If the number is callable, the primary action view should have a non-null tag.
+                assertNotNull(mItem.primaryActionView.getTag());
+
+                IntentProvider intentProvider = (IntentProvider)mItem.primaryActionView.getTag();
+                Intent callIntent = intentProvider.getIntent(mActivity);
+
+                //The intent should be to make the call
+                assertEquals(Intent.ACTION_CALL_PRIVILEGED, callIntent.getAction());
             }
         }
     }
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index a10dec9..7e4736e 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -24,6 +24,7 @@
 
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.PhoneCallDetailsHelper;
+import com.android.dialer.R;
 
 /**
  * Unit tests for {@link CallLogListItemHelper}.
@@ -52,19 +53,21 @@
     private PhoneNumberDisplayHelper mPhoneNumberHelper;
     private PhoneNumberDisplayHelper mPhoneNumberDisplayHelper;
 
+    private Resources mResources;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         Context context = getContext();
-        Resources resources = context.getResources();
-        CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
+        mResources = context.getResources();
+        CallTypeHelper callTypeHelper = new CallTypeHelper(mResources);
         final TestPhoneNumberUtilsWrapper phoneUtils = new TestPhoneNumberUtilsWrapper(
                 TEST_VOICEMAIL_NUMBER);
         PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
-                resources, callTypeHelper, phoneUtils);
-        mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(resources);
+                mResources, callTypeHelper, phoneUtils);
+        mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(mResources);
         mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberDisplayHelper,
-                resources);
+                mResources);
         mViews = CallLogListItemViews.createForTest(context);
     }
 
@@ -78,92 +81,286 @@
     public void testSetPhoneCallDetails() {
         setPhoneCallDetailsWithNumber("12125551234", Calls.PRESENTATION_ALLOWED,
                 "1-212-555-1234");
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites() {
         setPhoneCallDetailsWithNumberInFavorites("12125551234", Calls.PRESENTATION_ALLOWED,
                 "1-212-555-1234");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Unknown() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Unknown() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_UNKNOWN, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Private() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Private() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_RESTRICTED, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_Payphone() {
         setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_Payphone() {
         setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_PAYPHONE, "");
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetails_VoicemailNumber() {
         setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
                 Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_ReadVoicemail() {
         setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_UnreadVoicemail() {
         setUnreadPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_VoicemailFromUnknown() {
         setPhoneCallDetailsWithNumberAndType("", Calls.PRESENTATION_UNKNOWN,
                 "", Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_VoicemailNumber() {
         setPhoneCallDetailsWithNumberInFavorites(TEST_VOICEMAIL_NUMBER,
                 Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
-        assertNoCallButton();
+        assertNoCallIntent();
     }
 
     public void testSetPhoneCallDetailsInFavorites_ReadVoicemail() {
         setPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_UnreadVoicemail() {
         setUnreadPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
     public void testSetPhoneCallDetailsInFavorites_VoicemailFromUnknown() {
         setPhoneCallDetailsWithNumberAndTypeInFavorites("", Calls.PRESENTATION_UNKNOWN,
                 "", Calls.VOICEMAIL_TYPE);
-        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
     }
 
-    /** Asserts that the whole call area is gone. */
-    private void assertNoCallButton() {
-        assertEquals(View.GONE, mViews.secondaryActionView.getVisibility());
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an answered unknown call is received.
+     */
+    public void testGetCallDescriptionID_UnknownAnswered() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_answered_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an missed unknown call is received.
+     */
+    public void testGetCallDescriptionID_UnknownMissed() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an missed unknown call is received and a voicemail was left.
+     */
+    public void testGetCallDescriptionID_UnknownVoicemail() {
+        PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_unknown_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an answered call from a known caller is received.
+     */
+    public void testGetCallDescriptionID_KnownAnswered() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_answered_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where a missed call from a known caller is received.
+     */
+    public void testGetCallDescriptionID_KnownMissed() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where a missed call from a known caller is received and a voicemail was left.
+     */
+    public void testGetCallDescriptionID_KnownVoicemail() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_return_missed_call,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an outgoing call is made to a known number and there is a history of
+     * only a single call for this caller.
+     */
+    public void testGetCallDescriptionID_OutgoingSingle() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_call_last,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescriptionID method used to get the accessibility description for calls.
+     * Test case where an outgoing call is made to a known number and there is a history of
+     * many calls for this caller.
+     */
+    public void testGetCallDescriptionID_OutgoingMultiple() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        assertEquals(R.string.description_call_last_multiple,
+                mHelper.getCallDescriptionStringID(details));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For outgoing calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailOutgoing() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For regular incoming calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailIncoming() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For regular missed calls, we should NOT have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_NoVoicemailMissed() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.MISSED_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * For voicemail calls, we should have "New Voicemail" in the description.
+     */
+    public void testGetCallDescription_Voicemail() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertTrue(description.toString()
+                .contains(this.mResources.getString(R.string.description_new_voicemail)));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * Test that the "X calls" message is not present if there is only a single call.
+     */
+    public void testGetCallDescription_NumCallsSingle() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+
+        // Rather than hard coding the "X calls" string message, we'll generate it with an empty
+        // number of calls, and trim the resulting string.  This gets us just the word "calls",
+        // and ensures any trivial changes to that string resource won't unnecessarily break
+        // the unit test.
+        assertFalse(description.toString()
+                .contains(this.mResources.getString(R.string.description_num_calls, "").trim()));
+    }
+
+    /**
+     * Test getCallDescription method used to get the accessibility description for calls.
+     * Test that the "X calls" message is present if there are many calls.
+     */
+    public void testGetCallDescription_NumCallsMultiple() {
+        PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+                TEST_FORMATTED_NUMBER,
+                TEST_COUNTRY_ISO, TEST_GEOCODE,
+                new int[]{Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+        CharSequence description = mHelper.getCallDescription(details);
+        assertTrue(description.toString()
+                .contains(this.mResources.getString(R.string.description_num_calls, 2)));
+    }
+
+    /** Asserts that the primary action view does not have a call intent. */
+    private void assertNoCallIntent() {
+        Object intentProvider = (IntentProvider)mViews.primaryActionView.getTag();
+        // The intent provider should be null as there is no ability to make a call.
+        assertNull(intentProvider);
     }
 
     /** Sets the details of a phone call using the specified phone number. */