Allow for multiple call icons in the call log.

This commit adds support for having multiple call icons on a single
entry, to handle group entries.

When the group is represented as text, we currently use only the first
call type, but, in a follow-up, I will use an additional counter.

Change-Id: Iaa9d1c84eb926c1500bf0a439d45ce59314bd198
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index a1d2bb7..d08b76a 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -304,8 +304,8 @@
                     setListAdapter(adapter);
                 }
                 mPhoneCallDetailsHelper.setPhoneCallDetails(mPhoneCallDetailsViews,
-                        new PhoneCallDetails(mNumber, numberText, callType, date, nameText,
-                                numberType, numberLabel), false);
+                        new PhoneCallDetails(mNumber, numberText, new int[]{ callType }, date,
+                                nameText, numberType, numberLabel), false);
 
                 loadContactPhotos(photoId);
             } else {
diff --git a/src/com/android/contacts/PhoneCallDetails.java b/src/com/android/contacts/PhoneCallDetails.java
index c5c37df..7b02a88 100644
--- a/src/com/android/contacts/PhoneCallDetails.java
+++ b/src/com/android/contacts/PhoneCallDetails.java
@@ -27,8 +27,12 @@
     public final CharSequence number;
     /** The formatted version of {@link #number}. */
     public final CharSequence formattedNumber;
-    /** The type of call, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}. */
-    public final int callType;
+    /**
+     * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
+     * <p>
+     * There might be multiple types if this represents a set of entries grouped together.
+     */
+    public final int[] callTypes;
     /** The date of the call, in milliseconds since the epoch. */
     public final long date;
     /** The name of the contact, or the empty string. */
@@ -39,17 +43,17 @@
     public final CharSequence numberLabel;
 
     /** Create the details for a call with a number not associated with a contact. */
-    public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int callType,
+    public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
             long date) {
-        this(number, formattedNumber, callType, date, "", 0, "");
+        this(number, formattedNumber, callTypes, date, "", 0, "");
     }
 
     /** Create the details for a call with a number associated with a contact. */
-    public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int callType,
+    public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
             long date, CharSequence name, int numberType, CharSequence numberLabel) {
         this.number = number;
         this.formattedNumber = formattedNumber;
-        this.callType = callType;
+        this.callTypes = callTypes;
         this.date = date;
         this.name = name;
         this.numberType = numberType;
diff --git a/src/com/android/contacts/PhoneCallDetailsHelper.java b/src/com/android/contacts/PhoneCallDetailsHelper.java
index 4605799..7f73b04 100644
--- a/src/com/android/contacts/PhoneCallDetailsHelper.java
+++ b/src/com/android/contacts/PhoneCallDetailsHelper.java
@@ -86,58 +86,22 @@
     public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details,
             boolean useIcons) {
         if (useIcons) {
-            final Drawable callTypeDrawable;
-            switch (details.callType) {
-                case Calls.INCOMING_TYPE:
-                    callTypeDrawable = mIncomingDrawable;
-                    break;
-
-                case Calls.OUTGOING_TYPE:
-                    callTypeDrawable = mOutgoingDrawable;
-                    break;
-
-                case Calls.MISSED_TYPE:
-                    callTypeDrawable = mMissedDrawable;
-                    break;
-
-                case Calls.VOICEMAIL_TYPE:
-                    callTypeDrawable = mVoicemailDrawable;
-                    break;
-
-                default:
-                    throw new IllegalArgumentException("invalid call type: " + details.callType);
-            }
-            ImageView callTypeImage = new ImageView(mContext);
-            callTypeImage.setImageDrawable(callTypeDrawable);
             views.callTypeIcons.removeAllViews();
-            views.callTypeIcons.addView(callTypeImage);
-
+            int count = details.callTypes.length;
+            for (int callType : details.callTypes) {
+                ImageView callTypeImage = new ImageView(mContext);
+                callTypeImage.setImageDrawable(getCallTypeDrawable(callType));
+                views.callTypeIcons.addView(callTypeImage);
+            }
             views.callTypeIcons.setVisibility(View.VISIBLE);
             views.callTypeText.setVisibility(View.GONE);
             views.callTypeSeparator.setVisibility(View.GONE);
         } else {
             String callTypeName;
-            switch (details.callType) {
-                case Calls.INCOMING_TYPE:
-                    callTypeName = mIncomingName;
-                    break;
-
-                case Calls.OUTGOING_TYPE:
-                    callTypeName = mOutgoingName;
-                    break;
-
-                case Calls.MISSED_TYPE:
-                    callTypeName = mMissedName;
-                    break;
-
-                case Calls.VOICEMAIL_TYPE:
-                    callTypeName = mVoicemailName;
-                    break;
-
-                default:
-                    throw new IllegalArgumentException("invalid call type: " + details.callType);
-            }
-            views.callTypeText.setText(callTypeName);
+            // Use the name of the first call type.
+            // TODO: We should update this to handle the text for multiple calls as well.
+            int callType = details.callTypes[0];
+            views.callTypeText.setText(getCallTypeText(callType));
             views.callTypeIcons.removeAllViews();
 
             views.callTypeText.setVisibility(View.VISIBLE);
@@ -167,7 +131,7 @@
         } else {
             nameText = details.name;
             CharSequence displayNumber = getDisplayNumber(details.number, details.formattedNumber);
-            if (details.callType != 0 && numberFormattedLabel != null) {
+            if (numberFormattedLabel != null) {
                 numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
                         numberFormattedLabel + " " + displayNumber, 0,
                         numberFormattedLabel.length(),
@@ -191,6 +155,46 @@
         }
     }
 
+    /** Returns the text used to represent the given call type. */
+    private String getCallTypeText(int callType) {
+        switch (callType) {
+            case Calls.INCOMING_TYPE:
+                return mIncomingName;
+
+            case Calls.OUTGOING_TYPE:
+                return mOutgoingName;
+
+            case Calls.MISSED_TYPE:
+                return mMissedName;
+
+            case Calls.VOICEMAIL_TYPE:
+                return mVoicemailName;
+
+            default:
+                throw new IllegalArgumentException("invalid call type: " + callType);
+        }
+    }
+
+    /** Returns the drawable of the icon associated with the given call type. */
+    private Drawable getCallTypeDrawable(int callType) {
+        switch (callType) {
+            case Calls.INCOMING_TYPE:
+                return mIncomingDrawable;
+
+            case Calls.OUTGOING_TYPE:
+                return mOutgoingDrawable;
+
+            case Calls.MISSED_TYPE:
+                return mMissedDrawable;
+
+            case Calls.VOICEMAIL_TYPE:
+                return mVoicemailDrawable;
+
+            default:
+                throw new IllegalArgumentException("invalid call type: " + callType);
+        }
+    }
+
     private CharSequence getDisplayNumber(CharSequence number, CharSequence formattedNumber) {
         if (TextUtils.isEmpty(number)) {
             return "";
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 1f8e84b..11f8965 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -635,12 +635,11 @@
          * @param c the cursor pointing to the entry in the call log
          * @param count the number of entries in the current item, greater than 1 if it is a group
          */
-        public void bindView(View view, Cursor c, int count) {
+        private void bindView(View view, Cursor c, int count) {
             final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
 
             String number = c.getString(CallLogQuery.NUMBER);
             long date = c.getLong(CallLogQuery.DATE);
-            int callType = c.getInt(CallLogQuery.CALL_TYPE);
             final String formattedNumber;
             String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
             // Store away the number so we can call it directly if you click on the call icon
@@ -697,11 +696,12 @@
                 views.callView.setVisibility(View.VISIBLE);
             }
 
+            int[] callTypes = getCallTypes(c, count);
             final PhoneCallDetails details;
             if (TextUtils.isEmpty(name)) {
-                details = new PhoneCallDetails(number, formattedNumber, callType, date);
+                details = new PhoneCallDetails(number, formattedNumber, callTypes, date);
             } else {
-                details = new PhoneCallDetails(number, formattedNumber, callType, date, name,
+                details = new PhoneCallDetails(number, formattedNumber, callTypes, date, name,
                         ntype, label);
             }
             mCallLogViewsHelper.setPhoneCallDetails(views, details , true);
@@ -718,6 +718,24 @@
             }
         }
 
+        /**
+         * Returns the call types for the given number of items in the cursor.
+         * <p>
+         * It uses the next {@code count} rows in the cursor to extract the types.
+         * <p>
+         * It position in the cursor is unchanged by this function.
+         */
+        private int[] getCallTypes(Cursor cursor, int count) {
+            int position = cursor.getPosition();
+            int[] callTypes = new int[count];
+            for (int index = 0; index < count; ++index) {
+                callTypes[index] = cursor.getInt(CallLogQuery.CALL_TYPE);
+                cursor.moveToNext();
+            }
+            cursor.moveToPosition(position);
+            return callTypes;
+        }
+
         private void bindQuickContact(QuickContactBadge view, long photoId, long contactId,
                 String lookupKey) {
             view.assignContactUri(getContactUri(contactId, lookupKey));
diff --git a/src/com/android/contacts/calllog/CallLogListItemHelper.java b/src/com/android/contacts/calllog/CallLogListItemHelper.java
index 462b0b3..e4630e9 100644
--- a/src/com/android/contacts/calllog/CallLogListItemHelper.java
+++ b/src/com/android/contacts/calllog/CallLogListItemHelper.java
@@ -61,8 +61,9 @@
             boolean useIcons) {
         mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details, useIcons);
         if (views.callView != null) {
+            // The type of icon, call or play, is determined by the first call in the group.
             views.callView.setImageDrawable(
-                    details.callType == Calls.VOICEMAIL_TYPE ? mPlayDrawable : mCallDrawable);
+                    details.callTypes[0] == Calls.VOICEMAIL_TYPE ? mPlayDrawable : mCallDrawable);
             views.callView.setVisibility(
                     canPlaceCallsTo(details.number) ? View.VISIBLE : View.INVISIBLE);
         }