Modifying voicemail call log date and time format

Bug: 22168682
Change-Id: Id0135ca03310f6c1587151af81c29b22e9d20d2b
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e5d32ea..e9bf1f4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -424,6 +424,15 @@
     <!-- A nicely formatted call duration displayed when viewing call details. For example "42 min 28 sec" -->
     <string name="callDetailsDurationFormat"><xliff:g id="minutes" example="42">%s</xliff:g> min <xliff:g id="seconds" example="28">%s</xliff:g> sec</string>
 
+    <!-- The string 'Today'. This value is used in the voicemailCallLogDateTimeFormat rather than an
+         explicit date string, e.g. Jul 25, 2014, in the event that a voicemail was created on the
+         current day -->
+    <string name="voicemailCallLogToday">@string/call_log_header_today</string>
+
+    <!-- A format string used for displaying the date and time for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM
+         The date will be replaced by 'Today' for voicemails created on the current day. For example: Today at 2:49 PM -->
+    <string name="voicemailCallLogDateTimeFormat"><xliff:g id="date" example="Jul 25, 2014">%1$s</xliff:g> at <xliff:g id="time" example="2:49 PM">%2$s</xliff:g></string>
+
     <!-- Dialog message which is shown when the user tries to make a phone call
          to prohibited phone numbers [CHAR LIMIT=NONE] -->
     <string name="dialog_phone_call_prohibited_message" msgid="4313552620858880999">Can\'t call this number</string>
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index 54324cd..3792056 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -16,11 +16,11 @@
 
 package com.android.dialer.calllog;
 
+import com.google.common.collect.Lists;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.telecom.PhoneAccount;
@@ -34,16 +34,15 @@
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.R;
 import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.PhoneNumberUtil;
-
-import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
+import java.util.Calendar;
 
 /**
  * Helper class to fill in the views in {@link PhoneCallDetailsViews}.
  */
 public class PhoneCallDetailsHelper {
+
     /** The maximum number of icons will be shown to represent the call types in a group. */
     private static final int MAX_CALL_TYPE_ICONS = 3;
 
@@ -53,6 +52,9 @@
     private Long mCurrentTimeMillisForTest;
     private final TelecomCallLogCache mTelecomCallLogCache;
 
+    /** Calendar used to construct dates */
+    private final Calendar mCalendar;
+
     /**
      * List of items to be concatenated together for accessibility descriptions
      */
@@ -72,6 +74,7 @@
         mContext = context;
         mResources = resources;
         mTelecomCallLogCache = telecomCallLogCache;
+        mCalendar = Calendar.getInstance();
     }
 
     /** Fills the call details views with content. */
@@ -178,6 +181,7 @@
      * 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).
      */
@@ -205,16 +209,62 @@
     }
 
     /**
-     * Get the call date/time of the call, relative to the current time.
-     * e.g. 3 minutes ago
+     * Get the call date/time of the call. For the call log this is relative to the current time.
+     * e.g. 3 minutes ago. For voicemail, see {@link #getGranularDateTime(PhoneCallDetails)}
+     *
      * @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);
+        if (details.callTypes[0] == Calls.VOICEMAIL_TYPE) {
+            return getGranularDateTime(details);
+        }
+
+        return DateUtils.getRelativeTimeSpanString(details.date, getCurrentTimeMillis(),
+                DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
+    }
+
+    /**
+     * Get the granular version of the call date/time of the call. The result is always in the form
+     * 'DATE at TIME'. The date value changes based on when the call was created.
+     *
+     * If created today, DATE is 'Today'
+     * If created this year, DATE is 'MMM dd'
+     * Otherwise, DATE is 'MMM dd, yyyy'
+     *
+     * TIME is the localized time format, e.g. 'hh:mm a' or 'HH:mm'
+     *
+     * @param details Call details to use
+     * @return String representing when the call occurred
+     */
+    public CharSequence getGranularDateTime(PhoneCallDetails details) {
+        return mResources.getString(R.string.voicemailCallLogDateTimeFormat,
+                getGranularDate(details.date),
+                DateUtils.formatDateTime(mContext, details.date, DateUtils.FORMAT_SHOW_TIME));
+    }
+
+    /**
+     * Get the granular version of the call date. See {@link #getGranularDateTime(PhoneCallDetails)}
+     */
+    private String getGranularDate(long date) {
+        if (DateUtils.isToday(date)) {
+            return mResources.getString(R.string.voicemailCallLogToday);
+        }
+        return DateUtils.formatDateTime(mContext, date, DateUtils.FORMAT_SHOW_DATE
+                | DateUtils.FORMAT_ABBREV_MONTH
+                | (shouldShowYear(date) ? DateUtils.FORMAT_SHOW_YEAR : DateUtils.FORMAT_NO_YEAR));
+    }
+
+    /**
+     * Determines whether the year should be shown for the given date
+     *
+     * @return {@code true} if date is within the current year, {@code false} otherwise
+     */
+    private boolean shouldShowYear(long date) {
+        mCalendar.setTimeInMillis(getCurrentTimeMillis());
+        int currentYear = mCalendar.get(Calendar.YEAR);
+        mCalendar.setTimeInMillis(date);
+        return currentYear != mCalendar.get(Calendar.YEAR);
     }
 
     /** Sets the text of the header view for the details page of a phone call. */
diff --git a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
index 3182502..be35ad0 100644
--- a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
@@ -41,6 +41,8 @@
     /** The date of the call log entry. */
     private static final long TEST_DATE =
         new GregorianCalendar(2011, 5, 3, 13, 0, 0).getTimeInMillis();
+    private static final long INJECTED_CURRENT_DATE =
+        new GregorianCalendar(2011, 5, 4, 13, 0, 0).getTimeInMillis();
     /** A test duration value for phone calls. */
     private static final long TEST_DURATION = 62300;
     /** The number of the caller/callee in the log entry. */
@@ -73,8 +75,7 @@
         final TestTelecomCallLogCache phoneUtils = new TestTelecomCallLogCache(
                 mContext, TEST_VOICEMAIL_NUMBER);
         mHelper = new PhoneCallDetailsHelper(mContext, resources, phoneUtils);
-        mHelper.setCurrentTimeForTest(
-                new GregorianCalendar(2011, 5, 4, 13, 0, 0).getTimeInMillis());
+        mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
         mViews = PhoneCallDetailsViews.createForTest(mContext);
         mNameView = new TextView(mContext);
         mLocaleTestUtils = new LocaleTestUtils(mContext);
@@ -111,10 +112,25 @@
         assertNameEqualsResource(R.string.voicemail);
     }
 
-    public void testSetPhoneCallDetails_Normal() {
-        setPhoneCallDetailsWithNumber("14125551212",
-                Calls.PRESENTATION_ALLOWED, "1-412-555-1212");
-        assertTrue(mViews.callLocationAndDate.getText().toString().contains("Yesterday"));
+    // Voicemail date string has 3 different formats depending on how long ago the call was placed
+    public void testSetVoicemailPhoneCallDetails_Today() {
+        setVoicemailPhoneCallDetailsWithDate(System.currentTimeMillis());
+        assertDateEquals("Today at");
+    }
+
+    public void testSetVoicemailPhoneCallDetails_WithinCurrentYear() {
+        mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
+        String formattedTestDate = "Jun 3 at 1:00 PM";
+        setVoicemailPhoneCallDetailsWithDate(TEST_DATE);
+        assertDateEquals(formattedTestDate);
+    }
+
+    public void testSetVoicemailPhoneCallDetails_OutsideCurrentYear() {
+        mHelper.setCurrentTimeForTest(INJECTED_CURRENT_DATE);
+        long testDate = new GregorianCalendar(2009, 5, 3, 13, 0, 0).getTimeInMillis();
+        String formattedTestDate = "Jun 3, 2009 at 1:00 PM";
+        setVoicemailPhoneCallDetailsWithDate(testDate);
+        assertDateEquals(formattedTestDate);
     }
 
     /** Asserts that a char sequence is actually a Spanned corresponding to the expected HTML. */
@@ -300,7 +316,6 @@
             assertEquals(id, mViews.callTypeIcons.getCallType(index));
         }
         assertEquals(View.VISIBLE, mViews.callTypeIcons.getVisibility());
-        assertTrue(mViews.callLocationAndDate.getText().toString().contains("Yesterday"));
     }
 
     /**
@@ -342,6 +357,13 @@
         mHelper.setPhoneCallDetails(mViews, details);
     }
 
+    private void setVoicemailPhoneCallDetailsWithDate(long date) {
+        PhoneCallDetails details = getPhoneCallDetails();
+        details.date = date;
+        details.callTypes = new int[] {Calls.VOICEMAIL_TYPE};
+        mHelper.setPhoneCallDetails(mViews, details);
+    }
+
     /** Sets the phone call details with default values and the given call types using icons. */
     private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
         PhoneCallDetails details = getPhoneCallDetails();