Replace text with icon for call type.

The design is to have an icon to indicate the call type instead of a
piece of text. Since we plan to have multiple icons for groups, use a
LinearLayout to store the actual icons.

Change-Id: I0055eca8eff8a9bc038aa6f152f486c18a6592c5
diff --git a/res/layout/call_log_phone_call_details.xml b/res/layout/call_log_phone_call_details.xml
index 71fd63e..e58de21 100644
--- a/res/layout/call_log_phone_call_details.xml
+++ b/res/layout/call_log_phone_call_details.xml
@@ -24,8 +24,8 @@
         android:layout_alignParentLeft="true"
         android:layout_alignParentBottom="true"
     />
-    <TextView
-        android:id="@+id/call_type"
+    <LinearLayout
+        android:id="@+id/call_types"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceSmall"
@@ -34,13 +34,23 @@
         android:layout_above="@id/number"
     />
     <TextView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?attr/call_detail_primary_text_color"
+        android:layout_toRightOf="@id/call_types"
+        android:layout_above="@id/number"
+        android:layout_marginLeft="?attr/call_detail_date_margin"
+    />
+    <TextView
         android:id="@+id/name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?attr/call_detail_primary_text_color"
         android:layout_alignParentLeft="true"
-        android:layout_above="@id/call_type"
+        android:layout_above="@id/call_types"
         android:paddingBottom="2dp"
     />
 </merge>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 801cc51..bb73840 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -57,6 +57,7 @@
         <item name="call_detail_primary_text_color">#FFFFFF</item>
         <item name="call_detail_action_icon_size">50dip</item>
         <item name="call_detail_secondary_background_color">#FFFFFF</item>
+        <item name="call_detail_date_margin">5dip</item>
     </style>
 
     <style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
@@ -141,6 +142,7 @@
         <attr name="call_detail_primary_text_color" format="color" />
         <attr name="call_detail_primary_background_color" format="color" />
         <attr name="call_detail_secondary_background_color" format="color" />
+        <attr name="call_detail_date_margin" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="CallLogActivity">
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 43376ca..f87f8a4 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -115,7 +115,12 @@
         mResources = getResources();
 
         mPhoneCallDetailsViews = PhoneCallDetailsViews.fromView(getWindow().getDecorView());
-        mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(getResources(), getVoicemailNumber());
+        mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(this, getResources(),
+                getVoicemailNumber(),
+                getResources().getDrawable(R.drawable.ic_call_log_list_incoming_call),
+                getResources().getDrawable(R.drawable.ic_call_log_list_outgoing_call),
+                getResources().getDrawable(R.drawable.ic_call_log_list_missed_call),
+                getResources().getDrawable(R.drawable.ic_call_log_list_voicemail));
         mCallActionView = findViewById(R.id.call);
         mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
         mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
diff --git a/src/com/android/contacts/PhoneCallDetailsHelper.java b/src/com/android/contacts/PhoneCallDetailsHelper.java
index fc96a89..78ca252 100644
--- a/src/com/android/contacts/PhoneCallDetailsHelper.java
+++ b/src/com/android/contacts/PhoneCallDetailsHelper.java
@@ -19,8 +19,10 @@
 import com.android.contacts.format.FormatUtils;
 import com.android.internal.telephony.CallerInfo;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.telephony.PhoneNumberUtils;
@@ -28,16 +30,25 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.view.View;
+import android.widget.ImageView;
 
 /**
  * Helper class to fill in the views in {@link PhoneCallDetailsViews}.
  */
 public class PhoneCallDetailsHelper {
+    private final Context mContext;
     private final Resources mResources;
     private final String mVoicemailNumber;
-    private final String mTypeIncomingText;
-    private final String mTypeOutgoingText;
-    private final String mTypeMissedText;
+    /** Icon for incoming calls. */
+    private final Drawable mIncomingDrawable;
+    /** Icon for outgoing calls. */
+    private final Drawable mOutgoingDrawable;
+    /** Icon for missed calls. */
+    private final Drawable mMissedDrawable;
+    /** Icon for voicemails. */
+    private final Drawable mVoicemailDrawable;
+    /** The injected current time in milliseconds since the epoch. Used only by tests. */
+    private Long mCurrentTimeMillisForTest;
 
     /**
      * Creates a new instance of the helper.
@@ -46,12 +57,16 @@
      *
      * @param resources used to look up strings
      */
-    public PhoneCallDetailsHelper(Resources resources, String voicemailNumber) {
+    public PhoneCallDetailsHelper(Context context, Resources resources, String voicemailNumber,
+            Drawable incomingDrawable, Drawable outgoingDrawable, Drawable missedDrawable,
+            Drawable voicemailDrawable) {
+        mContext = context;
         mResources = resources;
         mVoicemailNumber = voicemailNumber;
-        mTypeIncomingText = mResources.getString(R.string.type_incoming);
-        mTypeOutgoingText = mResources.getString(R.string.type_outgoing);
-        mTypeMissedText = mResources.getString(R.string.type_missed);
+        mIncomingDrawable = incomingDrawable;
+        mOutgoingDrawable = outgoingDrawable;
+        mMissedDrawable = missedDrawable;
+        mVoicemailDrawable = voicemailDrawable;
     }
 
     /**
@@ -67,31 +82,30 @@
     public void setPhoneCallDetails(PhoneCallDetailsViews views, long date,
             int callType, CharSequence name, CharSequence number, int numberType,
             CharSequence numberLabel) {
-        CharSequence callTypeText = "";
+        Drawable callTypeDrawable = null;
         switch (callType) {
             case Calls.INCOMING_TYPE:
-                callTypeText = mTypeIncomingText;
+                callTypeDrawable = mIncomingDrawable;
                 break;
 
             case Calls.OUTGOING_TYPE:
-                callTypeText = mTypeOutgoingText;
+                callTypeDrawable = mOutgoingDrawable;
                 break;
 
             case Calls.MISSED_TYPE:
-                callTypeText = mTypeMissedText;
+                callTypeDrawable = mMissedDrawable;
+                break;
+
+            case Calls.VOICEMAIL_TYPE:
+                callTypeDrawable = mVoicemailDrawable;
                 break;
         }
-
         CharSequence shortDateText =
             DateUtils.getRelativeTimeSpanString(date,
-                    System.currentTimeMillis(),
+                    getCurrentTimeMillis(),
                     DateUtils.MINUTE_IN_MILLIS,
                     DateUtils.FORMAT_ABBREV_RELATIVE);
 
-        CharSequence callTypeAndDateText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
-                callTypeText + " " + shortDateText, 0, callTypeText.length(),
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-
         CharSequence numberFormattedLabel = null;
         // Only show a label if the number is shown and it is not a SIP address.
         if (!TextUtils.isEmpty(number) && !PhoneNumberUtils.isUriNumber(number.toString())) {
@@ -114,17 +128,22 @@
             }
         }
 
-        views.mCallTypeAndDateView.setText(callTypeAndDateText);
-        views.mCallTypeAndDateView.setVisibility(View.VISIBLE);
-        views.mNameView.setText(nameText);
-        views.mNameView.setVisibility(View.VISIBLE);
+        ImageView callTypeImage = new ImageView(mContext);
+        callTypeImage.setImageDrawable(callTypeDrawable);
+        views.callTypesLayout.removeAllViews();
+        views.callTypesLayout.addView(callTypeImage);
+
+        views.dateView.setText(shortDateText);
+        views.dateView.setVisibility(View.VISIBLE);
+        views.nameView.setText(nameText);
+        views.nameView.setVisibility(View.VISIBLE);
         // Do not show the number if it is not available. This happens if we have only the number,
         // in which case the number is shown in the name field instead.
         if (!TextUtils.isEmpty(numberText)) {
-            views.mNumberView.setText(numberText);
-            views.mNumberView.setVisibility(View.VISIBLE);
+            views.numberView.setText(numberText);
+            views.numberView.setVisibility(View.VISIBLE);
         } else {
-            views.mNumberView.setVisibility(View.GONE);
+            views.numberView.setVisibility(View.GONE);
         }
     }
 
@@ -146,4 +165,21 @@
         }
         return number;
     }
+
+    public void setCurrentTimeForTest(long currentTimeMillis) {
+        mCurrentTimeMillisForTest = currentTimeMillis;
+    }
+
+    /**
+     * Returns the current time in milliseconds since the epoch.
+     * <p>
+     * It can be injected in tests using {@link #setCurrentTimeForTest(long)}.
+     */
+    private long getCurrentTimeMillis() {
+        if (mCurrentTimeMillisForTest == null) {
+            return System.currentTimeMillis();
+        } else {
+            return mCurrentTimeMillisForTest;
+        }
+    }
 }
diff --git a/src/com/android/contacts/PhoneCallDetailsViews.java b/src/com/android/contacts/PhoneCallDetailsViews.java
index c2819c3..483ec65 100644
--- a/src/com/android/contacts/PhoneCallDetailsViews.java
+++ b/src/com/android/contacts/PhoneCallDetailsViews.java
@@ -17,37 +17,42 @@
 package com.android.contacts;
 
 import android.view.View;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
  * Encapsulates the views that are used to display the details of a phone call in the call log.
  */
 public final class PhoneCallDetailsViews {
-    public final TextView mNameView;
-    public final TextView mCallTypeAndDateView;
-    public final TextView mNumberView;
+    public final TextView nameView;
+    public final LinearLayout callTypesLayout;
+    public final TextView dateView;
+    public final TextView numberView;
 
-    private PhoneCallDetailsViews(TextView nameView, TextView callTypeAndDateView,
-            TextView numberView) {
-        mNameView = nameView;
-        mCallTypeAndDateView = callTypeAndDateView;
-        mNumberView = numberView;
+    private PhoneCallDetailsViews(TextView nameView, LinearLayout callTypesLayout,
+            TextView dateView, TextView numberView) {
+        this.nameView = nameView;
+        this.callTypesLayout = callTypesLayout;
+        this.dateView = dateView;
+        this.numberView = numberView;
     }
 
     /**
      * Create a new instance by extracting the elements from the given view.
      * <p>
      * The view should contain three text views with identifiers {@code R.id.name},
-     * {@code R.id.call_type}, and {@code R.id.number}.
+     * {@code R.id.date}, and {@code R.id.number}, and a linear layout with identifier
+     * {@code R.id.call_types}.
      */
     public static PhoneCallDetailsViews fromView(View view) {
         return new PhoneCallDetailsViews((TextView) view.findViewById(R.id.name),
-                (TextView) view.findViewById(R.id.call_type),
+                (LinearLayout) view.findViewById(R.id.call_types),
+                (TextView) view.findViewById(R.id.date),
                 (TextView) view.findViewById(R.id.number));
     }
 
     public static PhoneCallDetailsViews createForTest(TextView nameView,
-            TextView callTypeAndDateView, TextView numberView) {
-        return new PhoneCallDetailsViews(nameView, callTypeAndDateView, numberView);
+            LinearLayout callTypesLayout, TextView dateView, TextView numberView) {
+        return new PhoneCallDetailsViews(nameView, callTypesLayout, dateView, numberView);
     }
 }
diff --git a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
index ab00382..5c02959 100644
--- a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
@@ -16,13 +16,22 @@
 
 package com.android.contacts;
 
+import com.android.contacts.util.LocaleTestUtils;
 import com.android.internal.telephony.CallerInfo;
 
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.provider.CallLog.Calls;
 import android.test.AndroidTestCase;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
 /**
  * Unit tests for {@link PhoneCallDetailsHelper}.
  */
@@ -31,6 +40,16 @@
     private static final String TEST_VOICEMAIL_NUMBER = "125";
     /** The date of the call log entry. */
     private static final long TEST_DATE = 1300000000;
+    /** The number of the caller/callee in the log entry. */
+    private static final String TEST_NUMBER = "1-412-555-5555";
+    /** A drawable to be used for incoming calls. */
+    private static final Drawable TEST_INCOMING_DRAWABLE = new ColorDrawable(Color.BLACK);
+    /** A drawable to be used for outgoing calls. */
+    private static final Drawable TEST_OUTGOING_DRAWABLE = new ColorDrawable(Color.BLUE);
+    /** A drawable to be used for missed calls. */
+    private static final Drawable TEST_MISSED_DRAWABLE = new ColorDrawable(Color.RED);
+    /** A drawable to be used for voicemails. */
+    private static final Drawable TEST_VOICEMAIL_DRAWABLE = new ColorDrawable(Color.CYAN);
 
     /** The object under test. */
     private PhoneCallDetailsHelper mHelper;
@@ -41,9 +60,11 @@
     protected void setUp() throws Exception {
         super.setUp();
         Context context = getContext();
-        mHelper = new PhoneCallDetailsHelper(context.getResources(), TEST_VOICEMAIL_NUMBER);
-        mViews = PhoneCallDetailsViews.createForTest(new TextView(context), new TextView(context),
-                new TextView(context));
+        mHelper = new PhoneCallDetailsHelper(context, context.getResources(),
+                TEST_VOICEMAIL_NUMBER, TEST_INCOMING_DRAWABLE, TEST_OUTGOING_DRAWABLE,
+                TEST_MISSED_DRAWABLE, TEST_VOICEMAIL_DRAWABLE);
+        mViews = PhoneCallDetailsViews.createForTest(new TextView(context),
+                new LinearLayout(context), new TextView(context), new TextView(context));
     }
 
     @Override
@@ -78,6 +99,47 @@
         assertNameEquals("1-412-555-1212");
     }
 
+    public void testSetPhoneCallDetails_Date() {
+        LocaleTestUtils localeTestUtils = new LocaleTestUtils(getContext());
+        localeTestUtils.setLocale(Locale.US);
+        try {
+            mHelper.setCurrentTimeForTest(
+                    new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis());
+
+            setPhoneCallDetailsWithDate(
+                    new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis());
+            assertDateEquals("0 mins ago");
+
+            setPhoneCallDetailsWithDate(
+                    new GregorianCalendar(2011, 5, 3, 12, 0, 0).getTimeInMillis());
+            assertDateEquals("1 hour ago");
+
+            setPhoneCallDetailsWithDate(
+                    new GregorianCalendar(2011, 5, 2, 13, 0, 0).getTimeInMillis());
+            assertDateEquals("yesterday");
+
+            setPhoneCallDetailsWithDate(
+                    new GregorianCalendar(2011, 5, 1, 13, 0, 0).getTimeInMillis());
+            assertDateEquals("2 days ago");
+        } finally {
+            localeTestUtils.restoreLocale();
+        }
+    }
+
+    public void testSetPhoneCallDetails_CallType() {
+        setPhoneCallDetailsWithCallType(Calls.INCOMING_TYPE);
+        assertCallTypeIconsEquals(TEST_INCOMING_DRAWABLE);
+
+        setPhoneCallDetailsWithCallType(Calls.OUTGOING_TYPE);
+        assertCallTypeIconsEquals(TEST_OUTGOING_DRAWABLE);
+
+        setPhoneCallDetailsWithCallType(Calls.MISSED_TYPE);
+        assertCallTypeIconsEquals(TEST_MISSED_DRAWABLE);
+
+        setPhoneCallDetailsWithCallType(Calls.VOICEMAIL_TYPE);
+        assertCallTypeIconsEquals(TEST_VOICEMAIL_DRAWABLE);
+    }
+
     /** Asserts that the name text field contains the value of the given string resource. */
     private void assertNameEqualsResource(int resId) {
         assertNameEquals(getContext().getString(resId));
@@ -85,11 +147,36 @@
 
     /** Asserts that the name text field contains the given string value. */
     private void assertNameEquals(String text) {
-        assertEquals(text, mViews.mNameView.getText().toString());
+        assertEquals(text, mViews.nameView.getText().toString());
+    }
+
+    /** Asserts that the date text field contains the given string value. */
+    private void assertDateEquals(String text) {
+        assertEquals(text, mViews.dateView.getText().toString());
+    }
+
+    /** Asserts that the call type linear layout contains the images with the given drawables. */
+    private void assertCallTypeIconsEquals(Drawable... drawables) {
+        assertEquals(drawables.length, mViews.callTypesLayout.getChildCount());
+        for (int index = 0; index < drawables.length; ++index) {
+            Drawable drawable = drawables[index];
+            ImageView imageView = (ImageView) mViews.callTypesLayout.getChildAt(index);
+            assertEquals(drawable, imageView.getDrawable());
+        }
     }
 
     /** Sets the phone call details with default values and the given number. */
     private void setPhoneCallDetailsWithNumber(String number) {
         mHelper.setPhoneCallDetails(mViews, TEST_DATE, Calls.INCOMING_TYPE, "", number, 0, "");
     }
+
+    /** Sets the phone call details with default values and the given date. */
+    private void setPhoneCallDetailsWithDate(long date) {
+        mHelper.setPhoneCallDetails(mViews, date, Calls.INCOMING_TYPE, "", TEST_NUMBER, 0, "");
+    }
+
+    /** Sets the phone call details with default values and the given call type. */
+    private void setPhoneCallDetailsWithCallType(int callType) {
+        mHelper.setPhoneCallDetails(mViews, TEST_DATE, callType, "", TEST_NUMBER, 0, "");
+    }
 }