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/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);
     }
 }