summaryrefslogtreecommitdiff
path: root/java/com
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-01-19 11:55:16 -0800
committerCopybara-Service <copybara-piper@google.com>2018-01-19 13:17:07 -0800
commitfdaa46618ce61344bc83a66590863d126c47b05f (patch)
treeeee4f125136bdb155919f8f17f9c9cc6d92fa00f /java/com
parent7e421825670b090d2fe035575769af751e908015 (diff)
Add the "Yesterday" header in the new call log
Bug: 70989598 Test: NewCallLogAdapterTest, CallLogDatesTest PiperOrigin-RevId: 182567571 Change-Id: Ieabbe709668d843334bc3bf4a128834fddb57cb8
Diffstat (limited to 'java/com')
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogAdapter.java129
-rw-r--r--java/com/android/dialer/calllog/ui/res/values/strings.xml5
-rw-r--r--java/com/android/dialer/calllogutils/CallLogDates.java53
3 files changed, 126 insertions, 61 deletions
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
index 5618c4d16..05a339978 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
@@ -34,14 +34,21 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
/** IntDef for the different types of rows that can be shown in the call log. */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RowType.HEADER_TODAY, RowType.HEADER_OLDER, RowType.CALL_LOG_ENTRY})
+ @IntDef({
+ RowType.HEADER_TODAY,
+ RowType.HEADER_YESTERDAY,
+ RowType.HEADER_OLDER,
+ RowType.CALL_LOG_ENTRY
+ })
@interface RowType {
/** Header that displays "Today". */
int HEADER_TODAY = 1;
+ /** Header that displays "Yesterday". */
+ int HEADER_YESTERDAY = 2;
/** Header that displays "Older". */
- int HEADER_OLDER = 2;
+ int HEADER_OLDER = 3;
/** A row representing a call log entry (which could represent one or more calls). */
- int CALL_LOG_ENTRY = 3;
+ int CALL_LOG_ENTRY = 4;
}
private final Clock clock;
@@ -49,9 +56,13 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
private Cursor cursor;
- /** Null when the "Today" header should not be displayed. */
+ /** Position of the "Today" header. Null when it should not be displayed. */
@Nullable private Integer todayHeaderPosition;
- /** Null when the "Older" header should not be displayed. */
+
+ /** Position of the "Yesterday" header. Null when it should not be displayed. */
+ @Nullable private Integer yesterdayHeaderPosition;
+
+ /** Position of the "Older" header. Null when it should not be displayed. */
@Nullable private Integer olderHeaderPosition;
NewCallLogAdapter(Context context, Cursor cursor, Clock clock) {
@@ -75,38 +86,49 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
}
private void setHeaderPositions() {
- // Calculate header adapter positions by reading cursor.
+ // If there are no rows to display, set all header positions to null.
+ if (!cursor.moveToFirst()) {
+ todayHeaderPosition = null;
+ yesterdayHeaderPosition = null;
+ olderHeaderPosition = null;
+ return;
+ }
+
long currentTimeMillis = clock.currentTimeMillis();
- if (cursor.moveToFirst()) {
- long firstTimestamp = CoalescedAnnotatedCallLogCursorLoader.getTimestamp(cursor);
- if (CallLogDates.isSameDay(currentTimeMillis, firstTimestamp)) {
- this.todayHeaderPosition = 0;
- int adapterPosition = 2; // Accounted for "Today" header and first row.
- while (cursor.moveToNext()) {
- long timestamp = CoalescedAnnotatedCallLogCursorLoader.getTimestamp(cursor);
-
- if (CallLogDates.isSameDay(currentTimeMillis, timestamp)) {
- adapterPosition++;
- } else {
- this.olderHeaderPosition = adapterPosition;
- return;
- }
- }
- this.olderHeaderPosition = null; // Didn't find any "Older" rows.
+
+ int numItemsInToday = 0;
+ int numItemsInYesterday = 0;
+ do {
+ long timestamp = CoalescedAnnotatedCallLogCursorLoader.getTimestamp(cursor);
+ long dayDifference = CallLogDates.getDayDifference(currentTimeMillis, timestamp);
+ if (dayDifference == 0) {
+ numItemsInToday++;
+ } else if (dayDifference == 1) {
+ numItemsInYesterday++;
} else {
- this.todayHeaderPosition = null; // Didn't find any "Today" rows.
- this.olderHeaderPosition = 0;
+ break;
}
- } else { // There are no rows, just need to set these because they are final.
- this.todayHeaderPosition = null;
- this.olderHeaderPosition = null;
+ } while (cursor.moveToNext());
+
+ if (numItemsInToday > 0) {
+ numItemsInToday++; // including the "Today" header;
+ }
+ if (numItemsInYesterday > 0) {
+ numItemsInYesterday++; // including the "Yesterday" header;
}
+
+ // Set all header positions.
+ // A header position will be null if there is no item to be displayed under that header.
+ todayHeaderPosition = numItemsInToday > 0 ? 0 : null;
+ yesterdayHeaderPosition = numItemsInYesterday > 0 ? numItemsInToday : null;
+ olderHeaderPosition = !cursor.isAfterLast() ? numItemsInToday + numItemsInYesterday : null;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, @RowType int viewType) {
switch (viewType) {
case RowType.HEADER_TODAY:
+ case RowType.HEADER_YESTERDAY:
case RowType.HEADER_OLDER:
return new HeaderViewHolder(
LayoutInflater.from(viewGroup.getContext())
@@ -124,29 +146,36 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
- if (viewHolder instanceof HeaderViewHolder) {
- HeaderViewHolder headerViewHolder = (HeaderViewHolder) viewHolder;
- @RowType int viewType = getItemViewType(position);
- if (viewType == RowType.HEADER_OLDER) {
- headerViewHolder.setHeader(R.string.new_call_log_header_older);
- } else if (viewType == RowType.HEADER_TODAY) {
- headerViewHolder.setHeader(R.string.new_call_log_header_today);
- } else {
+ @RowType int viewType = getItemViewType(position);
+ switch (viewType) {
+ case RowType.HEADER_TODAY:
+ ((HeaderViewHolder) viewHolder).setHeader(R.string.new_call_log_header_today);
+ break;
+ case RowType.HEADER_YESTERDAY:
+ ((HeaderViewHolder) viewHolder).setHeader(R.string.new_call_log_header_yesterday);
+ break;
+ case RowType.HEADER_OLDER:
+ ((HeaderViewHolder) viewHolder).setHeader(R.string.new_call_log_header_older);
+ break;
+ case RowType.CALL_LOG_ENTRY:
+ NewCallLogViewHolder newCallLogViewHolder = (NewCallLogViewHolder) viewHolder;
+ int previousHeaders = 0;
+ if (todayHeaderPosition != null && position > todayHeaderPosition) {
+ previousHeaders++;
+ }
+ if (yesterdayHeaderPosition != null && position > yesterdayHeaderPosition) {
+ previousHeaders++;
+ }
+ if (olderHeaderPosition != null && position > olderHeaderPosition) {
+ previousHeaders++;
+ }
+ cursor.moveToPosition(position - previousHeaders);
+ newCallLogViewHolder.bind(cursor);
+ break;
+ default:
throw Assert.createIllegalStateFailException(
"Unexpected view type " + viewType + " at position: " + position);
- }
- return;
- }
- NewCallLogViewHolder newCallLogViewHolder = (NewCallLogViewHolder) viewHolder;
- int previousHeaders = 0;
- if (todayHeaderPosition != null && position > todayHeaderPosition) {
- previousHeaders++;
- }
- if (olderHeaderPosition != null && position > olderHeaderPosition) {
- previousHeaders++;
}
- cursor.moveToPosition(position - previousHeaders);
- newCallLogViewHolder.bind(cursor);
}
@Override
@@ -155,6 +184,9 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
if (todayHeaderPosition != null && position == todayHeaderPosition) {
return RowType.HEADER_TODAY;
}
+ if (yesterdayHeaderPosition != null && position == yesterdayHeaderPosition) {
+ return RowType.HEADER_YESTERDAY;
+ }
if (olderHeaderPosition != null && position == olderHeaderPosition) {
return RowType.HEADER_OLDER;
}
@@ -167,6 +199,9 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
if (todayHeaderPosition != null) {
numberOfHeaders++;
}
+ if (yesterdayHeaderPosition != null) {
+ numberOfHeaders++;
+ }
if (olderHeaderPosition != null) {
numberOfHeaders++;
}
diff --git a/java/com/android/dialer/calllog/ui/res/values/strings.xml b/java/com/android/dialer/calllog/ui/res/values/strings.xml
index 0ef0eaf26..ebddc3578 100644
--- a/java/com/android/dialer/calllog/ui/res/values/strings.xml
+++ b/java/com/android/dialer/calllog/ui/res/values/strings.xml
@@ -20,7 +20,10 @@
<!-- Header in call log to group calls from the current day. [CHAR LIMIT=30] -->
<string name="new_call_log_header_today">Today</string>
- <!-- Header in call log to group calls from before the current day. [CHAR LIMIT=30] -->
+ <!-- Header in call log to group calls from the previous day. [CHAR LIMIT=30] -->
+ <string name="new_call_log_header_yesterday">Yesterday</string>
+
+ <!-- Header in call log to group calls from before yesterday. [CHAR LIMIT=30] -->
<string name="new_call_log_header_older">Older</string>
</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/calllogutils/CallLogDates.java b/java/com/android/dialer/calllogutils/CallLogDates.java
index 82e8e404e..84e52df14 100644
--- a/java/com/android/dialer/calllogutils/CallLogDates.java
+++ b/java/com/android/dialer/calllogutils/CallLogDates.java
@@ -50,7 +50,7 @@ public final class CallLogDates {
return DateUtils.formatDateTime(
context, timestampMillis, DateUtils.FORMAT_SHOW_TIME); // e.g. 12:15 PM
}
- if (isWithin3Days(nowMillis, timestampMillis)) {
+ if (getDayDifference(nowMillis, timestampMillis) < 3) {
return formatDayOfWeek(context, timestampMillis); // e.g. "Wednesday"
}
return formatAbbreviatedMonthAndDay(context, timestampMillis); // e.g. "Jan 15"
@@ -129,26 +129,53 @@ public final class CallLogDates {
UCharacter.TITLECASE_NO_LOWERCASE);
}
- private static boolean isWithin3Days(long nowMillis, long timestampMillis) {
- Calendar threeDaysAgoStartOfDay = Calendar.getInstance();
- threeDaysAgoStartOfDay.setTimeInMillis(nowMillis);
+ /**
+ * Returns the absolute difference in days between two timestamps. It is the caller's
+ * responsibility to ensure both timestamps are in milliseconds. Failure to do so will result in
+ * undefined behavior.
+ *
+ * <p>Note that the difference is based on day boundaries, not 24-hour periods.
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>The difference between 01/19/2018 00:00 and 01/19/2018 23:59 is 0.
+ * <li>The difference between 01/18/2018 23:59 and 01/19/2018 23:59 is 1.
+ * <li>The difference between 01/18/2018 00:00 and 01/19/2018 23:59 is 1.
+ * <li>The difference between 01/17/2018 23:59 and 01/19/2018 00:00 is 2.
+ * </ul>
+ */
+ public static int getDayDifference(long firstTimestamp, long secondTimestamp) {
+ // Ensure secondMillis is no less than firstMillis
+ if (secondTimestamp < firstTimestamp) {
+ long t = firstTimestamp;
+ firstTimestamp = secondTimestamp;
+ secondTimestamp = t;
+ }
- // This is attempting to find the start of the current day, but it's not quite right due to
+ // Use secondTimestamp as reference
+ Calendar startOfReferenceDay = Calendar.getInstance();
+ startOfReferenceDay.setTimeInMillis(secondTimestamp);
+
+ // This is attempting to find the start of the reference day, but it's not quite right due to
// daylight savings. Unfortunately there doesn't seem to be a way to get the correct start of
// the day without using Joda or Java8, both of which are disallowed. This means that the wrong
// formatting may be applied on days with time changes (though the displayed values will be
// correct).
- threeDaysAgoStartOfDay.add(
- Calendar.HOUR_OF_DAY, -threeDaysAgoStartOfDay.get(Calendar.HOUR_OF_DAY));
- threeDaysAgoStartOfDay.add(Calendar.MINUTE, -threeDaysAgoStartOfDay.get(Calendar.MINUTE));
- threeDaysAgoStartOfDay.add(Calendar.SECOND, -threeDaysAgoStartOfDay.get(Calendar.SECOND));
+ startOfReferenceDay.add(Calendar.HOUR_OF_DAY, -startOfReferenceDay.get(Calendar.HOUR_OF_DAY));
+ startOfReferenceDay.add(Calendar.MINUTE, -startOfReferenceDay.get(Calendar.MINUTE));
+ startOfReferenceDay.add(Calendar.SECOND, -startOfReferenceDay.get(Calendar.SECOND));
- threeDaysAgoStartOfDay.add(Calendar.DATE, -2);
+ Calendar other = Calendar.getInstance();
+ other.setTimeInMillis(firstTimestamp);
- Calendar then = Calendar.getInstance();
- then.setTimeInMillis(timestampMillis);
+ int dayDifference = 0;
+ while (other.before(startOfReferenceDay)) {
+ startOfReferenceDay.add(Calendar.DATE, -1);
+ dayDifference++;
+ }
- return then.equals(threeDaysAgoStartOfDay) || then.after(threeDaysAgoStartOfDay);
+ return dayDifference;
}
/** Returns true if the provided timestamps are from the same day in the default time zone. */