From 5680b01ecb566e60a63c3a3362ec31f912cef692 Mon Sep 17 00:00:00 2001 From: linyuh Date: Mon, 22 Jan 2018 11:33:01 -0800 Subject: Properly display date and/or time of entries in the new call log. Bug: 70989595 Test: CallLogDatesTest, CallLogEntryTextTest PiperOrigin-RevId: 182809700 Change-Id: I84b699536ae7f77e6c27db0b1b008e3ebc6216c2 --- .../android/dialer/calllogutils/CallLogDates.java | 117 +++++++++++++++------ .../dialer/calllogutils/CallLogEntryText.java | 31 +++--- .../dialer/calllogutils/res/values/strings.xml | 2 +- .../voicemail/listui/NewVoicemailAdapter.java | 6 +- 4 files changed, 104 insertions(+), 52 deletions(-) (limited to 'java') diff --git a/java/com/android/dialer/calllogutils/CallLogDates.java b/java/com/android/dialer/calllogutils/CallLogDates.java index 84e52df14..bdf621518 100644 --- a/java/com/android/dialer/calllogutils/CallLogDates.java +++ b/java/com/android/dialer/calllogutils/CallLogDates.java @@ -35,29 +35,52 @@ public final class CallLogDates { *

Rules: * *

-   *   if < 1 minute ago: "Now";
-   *   else if today: "12:15 PM"
-   *   else if < 3 days ago: "Wednesday";
-   *   else: "Jan 15"
+   *   if < 1 minute ago: "Just now";
+   *   else if < 1 hour ago: time relative to now (e.g., "8 min. ago");
+   *   else if today: time (e.g., "12:15 PM");
+   *   else if < 7 days: abbreviated day of week (e.g., "Wed");
+   *   else if < 1 year: date with abbreviated month, day, but no year (e.g., "Jan 15");
+   *   else: date with abbreviated month, day, and year (e.g., "Jan 15, 2018").
    * 
*/ public static CharSequence newCallLogTimestampLabel( Context context, long nowMillis, long timestampMillis) { + // For calls logged less than 1 minute ago, display "Just now". if (nowMillis - timestampMillis < TimeUnit.MINUTES.toMillis(1)) { - return context.getString(R.string.now); + return context.getString(R.string.just_now); } - if (isSameDay(nowMillis, timestampMillis)) { - return DateUtils.formatDateTime( - context, timestampMillis, DateUtils.FORMAT_SHOW_TIME); // e.g. 12:15 PM + + // For calls logged less than 1 hour ago, display time relative to now (e.g., "8 min. ago"). + if (nowMillis - timestampMillis < TimeUnit.HOURS.toMillis(1)) { + return DateUtils.getRelativeTimeSpanString( + timestampMillis, nowMillis, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE); + } + + int dayDifference = getDayDifference(nowMillis, timestampMillis); + + // For calls logged today, display time (e.g., "12:15 PM"). + if (dayDifference == 0) { + return DateUtils.formatDateTime(context, timestampMillis, DateUtils.FORMAT_SHOW_TIME); } - if (getDayDifference(nowMillis, timestampMillis) < 3) { - return formatDayOfWeek(context, timestampMillis); // e.g. "Wednesday" + + // For calls logged within a week, display the abbreviated day of week (e.g., "Wed"). + if (dayDifference < 7) { + return formatDayOfWeek(context, timestampMillis); + } + + // For calls logged within a year, display abbreviated month, day, but no year (e.g., "Jan 15"). + if (isWithinOneYear(nowMillis, timestampMillis)) { + return formatAbbreviatedDate(context, timestampMillis, /* showYear = */ false); } - return formatAbbreviatedMonthAndDay(context, timestampMillis); // e.g. "Jan 15" + + // For calls logged no less than one year ago, display abbreviated month, day, and year + // (e.g., "Jan 15, 2018"). + return formatAbbreviatedDate(context, timestampMillis, /* showYear = */ true); } /** - * Formats the provided date into a value suitable for display in the current locale. + * Formats the provided timestamp (in milliseconds) into date and time suitable for display in the + * current locale. * *

For example, returns a string like "Wednesday, May 25, 2016, 8:02PM" or "Chorshanba, 2016 * may 25,20:02". @@ -65,11 +88,11 @@ public final class CallLogDates { *

For pre-N devices, the returned value may not start with a capital if the local convention * is to not capitalize day names. On N+ devices, the returned value is always capitalized. */ - public static CharSequence formatDate(Context context, long callDateMillis) { + public static CharSequence formatDate(Context context, long timestamp) { return toTitleCase( DateUtils.formatDateTime( context, - callDateMillis, + timestamp, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY @@ -77,30 +100,36 @@ public final class CallLogDates { } /** - * Formats the provided date into the day of week. + * Formats the provided timestamp (in milliseconds) into abbreviated day of week. * - *

For example, returns a string like "Wednesday" or "Chorshanba". + *

For example, returns a string like "Wed" or "Chor". * *

For pre-N devices, the returned value may not start with a capital if the local convention * is to not capitalize day names. On N+ devices, the returned value is always capitalized. */ - private static CharSequence formatDayOfWeek(Context context, long callDateMillis) { + private static CharSequence formatDayOfWeek(Context context, long timestamp) { return toTitleCase( - DateUtils.formatDateTime(context, callDateMillis, DateUtils.FORMAT_SHOW_WEEKDAY)); + DateUtils.formatDateTime( + context, timestamp, DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY)); } /** - * Formats the provided date into the month abbreviation and day. + * Formats the provided timestamp (in milliseconds) into the month abbreviation, day, and + * optionally, year. * - *

For example, returns a string like "Jan 15". + *

For example, returns a string like "Jan 15" or "Jan 15, 2018". * *

For pre-N devices, the returned value may not start with a capital if the local convention * is to not capitalize day names. On N+ devices, the returned value is always capitalized. */ - private static CharSequence formatAbbreviatedMonthAndDay(Context context, long callDateMillis) { - return toTitleCase( - DateUtils.formatDateTime( - context, callDateMillis, DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR)); + private static CharSequence formatAbbreviatedDate( + Context context, long timestamp, boolean showYear) { + int flags = DateUtils.FORMAT_ABBREV_MONTH; + if (!showYear) { + flags |= DateUtils.FORMAT_NO_YEAR; + } + + return toTitleCase(DateUtils.formatDateTime(context, timestamp, flags)); } private static CharSequence toTitleCase(CharSequence value) { @@ -146,7 +175,7 @@ public final class CallLogDates { * */ public static int getDayDifference(long firstTimestamp, long secondTimestamp) { - // Ensure secondMillis is no less than firstMillis + // Ensure secondTimestamp is no less than firstTimestamp if (secondTimestamp < firstTimestamp) { long t = firstTimestamp; firstTimestamp = secondTimestamp; @@ -178,16 +207,36 @@ public final class CallLogDates { return dayDifference; } - /** Returns true if the provided timestamps are from the same day in the default time zone. */ - public static boolean isSameDay(long firstMillis, long secondMillis) { - Calendar first = Calendar.getInstance(); - first.setTimeInMillis(firstMillis); + /** + * Returns true if the two timestamps are within one year. It is the caller's responsibility to + * ensure both timestamps are in milliseconds. Failure to do so will result in undefined behavior. + * + *

Note that the difference is based on 365/366-day periods. + * + *

Examples: + * + *

+ */ + private static boolean isWithinOneYear(long firstTimestamp, long secondTimestamp) { + // Ensure secondTimestamp is no less than firstTimestamp + if (secondTimestamp < firstTimestamp) { + long t = firstTimestamp; + firstTimestamp = secondTimestamp; + secondTimestamp = t; + } + + // Use secondTimestamp as reference + Calendar reference = Calendar.getInstance(); + reference.setTimeInMillis(secondTimestamp); + reference.add(Calendar.YEAR, -1); - Calendar second = Calendar.getInstance(); - second.setTimeInMillis(secondMillis); + Calendar other = Calendar.getInstance(); + other.setTimeInMillis(firstTimestamp); - return first.get(Calendar.YEAR) == second.get(Calendar.YEAR) - && first.get(Calendar.MONTH) == second.get(Calendar.MONTH) - && first.get(Calendar.DAY_OF_MONTH) == second.get(Calendar.DAY_OF_MONTH); + return reference.before(other); } } diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java index 1df44583f..25fe86452 100644 --- a/java/com/android/dialer/calllogutils/CallLogEntryText.java +++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java @@ -49,22 +49,25 @@ public final class CallLogEntryText { return primaryText.toString(); } - /** The secondary text to show in the main call log entry list. */ + /** + * The secondary text to show in the main call log entry list. + * + *

Rules: (Duo video, )?$Label|$Location • Date + * + *

Examples: + * + *

+ * + *

See {@link CallLogDates#newCallLogTimestampLabel(Context, long, long)} for date rules. + */ public static CharSequence buildSecondaryTextForEntries( Context context, Clock clock, CoalescedRow row) { - /* - * Rules: (Duo video, )?$Label|$Location • Date - * - * Examples: - * Duo Video, Mobile • Now - * Duo Video • 11:45pm - * Mobile • 11:45pm - * Mobile • Sunday - * Brooklyn, NJ • Jan 15 - * - * Date rules: - * if < 1 minute ago: "Now"; else if today: HH:MM(am|pm); else if < 3 days: day; else: MON D - */ StringBuilder secondaryText = secondaryTextPrefix(context, row); if (secondaryText.length() > 0) { diff --git a/java/com/android/dialer/calllogutils/res/values/strings.xml b/java/com/android/dialer/calllogutils/res/values/strings.xml index b8ba5b1f3..8784bf8c9 100644 --- a/java/com/android/dialer/calllogutils/res/values/strings.xml +++ b/java/com/android/dialer/calllogutils/res/values/strings.xml @@ -129,7 +129,7 @@ Voicemail - Now + Just now Video diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java index 86d386050..c9bf6e1a8 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java @@ -104,7 +104,7 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter private final NewVoicemailMediaPlayer mediaPlayer = new NewVoicemailMediaPlayer(new MediaPlayer()); - /** @param cursor whose projection is {@link VoicemailCursorLoader.VOICEMAIL_COLUMNS} */ + /** @param cursor whose projection is {@link VoicemailCursorLoader#VOICEMAIL_COLUMNS} */ NewVoicemailAdapter(Cursor cursor, Clock clock, FragmentManager fragmentManager) { LogUtil.enterBlock("NewVoicemailAdapter"); this.cursor = cursor; @@ -133,14 +133,14 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter long currentTimeMillis = clock.currentTimeMillis(); if (cursor.moveToNext()) { long firstTimestamp = VoicemailCursorLoader.getTimestamp(cursor); - if (CallLogDates.isSameDay(currentTimeMillis, firstTimestamp)) { + if (CallLogDates.getDayDifference(currentTimeMillis, firstTimestamp) == 0) { this.todayHeaderPosition = 0 + alertOffSet; int adapterPosition = 2 + alertOffSet; // Accounted for the "Alert", "Today" header and first row. while (cursor.moveToNext()) { long timestamp = VoicemailCursorLoader.getTimestamp(cursor); - if (CallLogDates.isSameDay(currentTimeMillis, timestamp)) { + if (CallLogDates.getDayDifference(currentTimeMillis, timestamp) == 0) { adapterPosition++; } else { this.olderHeaderPosition = adapterPosition; -- cgit v1.2.3