diff options
Diffstat (limited to 'src')
3 files changed, 219 insertions, 13 deletions
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index 0aca9136b..c4389ad49 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -40,6 +40,7 @@ import android.widget.Toast; import com.android.common.widget.GroupingListAdapter; import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; +import com.android.contacts.common.util.DateUtils; import com.android.contacts.common.util.UriUtils; import com.android.dialer.PhoneCallDetails; import com.android.dialer.PhoneCallDetailsHelper; @@ -60,7 +61,6 @@ import java.util.LinkedList; public class CallLogAdapter extends GroupingListAdapter implements ViewTreeObserver.OnPreDrawListener, CallLogGroupBuilder.GroupCreator { - /** The enumeration of {@link android.os.AsyncTask} objects used in this class. */ public enum Tasks { REMOVE_CALL_LOG_ENTRIES, @@ -117,6 +117,12 @@ public class CallLogAdapter extends GroupingListAdapter /** The size of the cache of contact info. */ private static final int CONTACT_INFO_CACHE_SIZE = 100; + /** Localized string representing the word "Today". */ + private static final CharSequence TODAY_LABEL = DateUtils.getTodayString(); + + /** Localized string representing the word "Yesterday". */ + private static final CharSequence YESTERDAY_LABEL = DateUtils.getYesterdayString(); + protected final Context mContext; private final ContactInfoHelper mContactInfoHelper; private final CallFetcher mCallFetcher; @@ -139,6 +145,20 @@ public class CallLogAdapter extends GroupingListAdapter private HashMap<Long,Boolean> mIsExpanded = new HashMap<Long,Boolean>(); /** + * Hashmap, keyed by call Id, used to track the day group for a call. As call log entries are + * put into the primary call groups in {@link com.android.dialer.calllog.CallLogGroupBuilder}, + * they are also assigned a secondary "day group". This hashmap tracks the day group assigned + * to all calls in the call log. This information is used to trigger the display of a day + * group header above the call log entry at the start of a day group. + * Note: Multiple calls are grouped into a single primary "call group" in the call log, and + * the cursor used to bind rows includes all of these calls. When determining if a day group + * change has occurred it is necessary to look at the last entry in the call log to determine + * its day group. This hashmap provides a means of determining the previous day group without + * having to reverse the cursor to the start of the previous day call log entry. + */ + private HashMap<Long,Integer> mDayGroups = new HashMap<Long, Integer>(); + + /** * A request for contact details for the given number. */ private static final class ContactInfoRequest { @@ -589,7 +609,6 @@ public class CallLogAdapter extends GroupingListAdapter // Default case: an item in the call log. views.primaryActionView.setVisibility(View.VISIBLE); - views.listHeaderTextView.setVisibility(View.GONE); final String number = c.getString(CallLogQuery.NUMBER); final int numberPresentation = c.getInt(CallLogQuery.NUMBER_PRESENTATION); @@ -600,6 +619,21 @@ public class CallLogAdapter extends GroupingListAdapter final long rowId = c.getLong(CallLogQuery.ID); views.rowId = rowId; + // For entries in the call log, check if the day group has changed and display a header + // if necessary. + if (mIsCallLog) { + int currentGroup = getDayGroupForCall(rowId); + int previousGroup = getPreviousDayGroup(c); + if (currentGroup != previousGroup) { + views.dayGroupHeader.setVisibility(View.VISIBLE); + views.dayGroupHeader.setText(getGroupDescription(currentGroup)); + } else { + views.dayGroupHeader.setVisibility(View.GONE); + } + } else { + views.dayGroupHeader.setVisibility(View.GONE); + } + // Store some values used when the actions ViewStub is inflated on expansion of the actions // section. views.number = number; @@ -738,6 +772,38 @@ public class CallLogAdapter extends GroupingListAdapter } /** + * Retrieves the day group of the previous call in the call log. Used to determine if the day + * group has changed and to trigger display of the day group text. + * + * @param cursor The call log cursor. + * @return The previous day group, or DAY_GROUP_NONE if this is the first call. + */ + private int getPreviousDayGroup(Cursor cursor) { + // We want to restore the position in the cursor at the end. + int startingPosition = cursor.getPosition(); + int dayGroup = CallLogGroupBuilder.DAY_GROUP_NONE; + if (cursor.moveToPrevious()) { + long previousRowId = cursor.getLong(CallLogQuery.ID); + dayGroup = getDayGroupForCall(previousRowId); + } + cursor.moveToPosition(startingPosition); + return dayGroup; + } + + /** + * Given a call Id, look up the day group that the call belongs to. The day group data is + * populated in {@link com.android.dialer.calllog.CallLogGroupBuilder}. + * + * @param callId The call to retrieve the day group for. + * @return The day group for the call. + */ + private int getDayGroupForCall(long callId) { + if (mDayGroups.containsKey(callId)) { + return mDayGroups.get(callId); + } + return CallLogGroupBuilder.DAY_GROUP_NONE; + } + /** * Determines if a call log row with the given Id is expanded to show the action buttons or * not. If the row Id is not yet tracked, add a new entry assuming the row is collapsed. * @param rowId @@ -779,9 +845,9 @@ public class CallLogAdapter extends GroupingListAdapter inflateActionViewStub(callLogItem); views.actionsView.setVisibility(View.VISIBLE); - callLogItem.setBackgroundColor( + views.callLogEntryView.setBackgroundColor( callLogItem.getResources().getColor(R.color.background_dialer_light)); - callLogItem.setElevation( + views.callLogEntryView.setElevation( callLogItem.getResources().getDimension(R.dimen.call_log_expanded_elevation)); // Attempt to give accessibility focus to one of the action buttons. @@ -799,9 +865,9 @@ public class CallLogAdapter extends GroupingListAdapter views.actionsView.setVisibility(View.GONE); } - callLogItem.setBackgroundColor( + views.callLogEntryView.setBackgroundColor( callLogItem.getResources().getColor(R.color.background_dialer_list_items)); - callLogItem.setElevation(0); + views.callLogEntryView.setElevation(0); } } @@ -1138,6 +1204,27 @@ public class CallLogAdapter extends GroupingListAdapter super.addGroup(cursorPosition, size, expanded); } + /** + * Stores the day group associated with a call in the call log. + * + * @param rowId The row Id of the current call. + * @param dayGroup The day group the call belongs in. + */ + @Override + public void setDayGroup(long rowId, int dayGroup) { + if (!mDayGroups.containsKey(rowId)) { + mDayGroups.put(rowId, dayGroup); + } + } + + /** + * Clears the day group associations on re-bind of the call log. + */ + @Override + public void clearDayGroups() { + mDayGroups.clear(); + } + /* * Get the number from the Contacts, if available, since sometimes * the number provided by caller id may not be formatted properly @@ -1199,6 +1286,24 @@ public class CallLogAdapter extends GroupingListAdapter } /** + * Determines the description for a day group. + * + * @param group The day group to retrieve the description for. + * @return The day group description. + */ + private CharSequence getGroupDescription(int group) { + if (group == CallLogGroupBuilder.DAY_GROUP_TODAY) { + return TODAY_LABEL; + } else if (group == CallLogGroupBuilder.DAY_GROUP_YESTERDAY) { + return YESTERDAY_LABEL; + } else if (group == CallLogGroupBuilder.DAY_GROUP_LAST_WEEK) { + return mContext.getResources().getString(R.string.call_log_header_last_week); + } else { + return mContext.getResources().getString(R.string.call_log_header_other); + } + } + + /** * Retrieves an instance of the asynchronous task executor, creating one if required. * @return The {@link com.android.dialer.util.AsyncTaskExecutor} */ diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java index 0b2edf0bb..50cf054a2 100644 --- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java +++ b/src/com/android/dialer/calllog/CallLogGroupBuilder.java @@ -19,22 +19,74 @@ package com.android.dialer.calllog; import android.database.Cursor; import android.provider.CallLog.Calls; import android.telephony.PhoneNumberUtils; +import android.text.format.Time; import com.android.common.widget.GroupingListAdapter; +import com.android.contacts.common.util.DateUtils; import com.android.contacts.common.util.PhoneNumberHelper; import com.google.common.annotations.VisibleForTesting; /** - * Groups together calls in the call log. + * Groups together calls in the call log. The primary grouping attempts to group together calls + * to and from the same number into a single row on the call log. + * A secondary grouping assigns calls, grouped via the primary grouping, to "day groups". The day + * groups provide a means of identifying the calls which occurred "Today", "Yesterday", "Last week", + * or "Other". * <p> * This class is meant to be used in conjunction with {@link GroupingListAdapter}. */ public class CallLogGroupBuilder { public interface GroupCreator { + + /** + * Defines the interface for adding a group to the call log. + * The primary group for a call log groups the calls together based on the number which was + * dialed. + * @param cursorPosition The starting position of the group in the cursor. + * @param size The size of the group. + * @param expanded Whether the group is expanded; always false for the call log. + */ public void addGroup(int cursorPosition, int size, boolean expanded); + + /** + * Defines the interface for tracking the day group each call belongs to. Calls in a call + * group are assigned the same day group as the first call in the group. The day group + * assigns calls to the buckets: Today, Yesterday, Last week, and Other + * + * @param rowId The row Id of the current call. + * @param dayGroup The day group the call belongs in. + */ + public void setDayGroup(long rowId, int dayGroup); + + /** + * Defines the interface for clearing the day groupings information on rebind/regroup. + */ + public void clearDayGroups(); } + /** + * Day grouping for call log entries used to represent no associated day group. Used primarily + * when retrieving the previous day group, but there is no previous day group (i.e. we are at + * the start of the list). + */ + public static final int DAY_GROUP_NONE = -1; + + /** Day grouping for calls which occurred today. */ + public static final int DAY_GROUP_TODAY = 0; + + /** Day grouping for calls which occurred yesterday. */ + public static final int DAY_GROUP_YESTERDAY = 1; + + /** Day grouping for calls which occurred last week. */ + public static final int DAY_GROUP_LAST_WEEK = 2; + + /** Day grouping for calls which occurred before last week. */ + public static final int DAY_GROUP_OTHER = 3; + + /** Instance of the time object used for time calculations. */ + private static final Time TIME = new Time(); + /** The object on which the groups are created. */ private final GroupCreator mGroupCreator; @@ -59,18 +111,33 @@ public class CallLogGroupBuilder { return; } + // Clear any previous day grouping information. + mGroupCreator.clearDayGroups(); + + // Get current system time, used for calculating which day group calls belong to. + long currentTime = System.currentTimeMillis(); + int currentGroupSize = 1; cursor.moveToFirst(); // The number of the first entry in the group. String firstNumber = cursor.getString(CallLogQuery.NUMBER); // This is the type of the first call in the group. int firstCallType = cursor.getInt(CallLogQuery.CALL_TYPE); + + // Determine the day group for the first call in the cursor. + final long firstDate = cursor.getLong(CallLogQuery.DATE); + final long firstRowId = cursor.getLong(CallLogQuery.ID); + int currentGroupDayGroup = getDayGroup(firstDate, currentTime); + mGroupCreator.setDayGroup(firstRowId, currentGroupDayGroup); + while (cursor.moveToNext()) { // The number of the current row in the cursor. final String currentNumber = cursor.getString(CallLogQuery.NUMBER); final int callType = cursor.getInt(CallLogQuery.CALL_TYPE); final boolean sameNumber = equalNumbers(firstNumber, currentNumber); final boolean shouldGroup; + final long currentCallId = cursor.getLong(CallLogQuery.ID); + final long date = cursor.getLong(CallLogQuery.DATE); if (!sameNumber) { // Should only group with calls from the same number. @@ -88,6 +155,11 @@ public class CallLogGroupBuilder { // the group until we find a call that does not match. currentGroupSize++; } else { + // The call group has changed, so determine the day group for the new call group. + // This ensures all calls grouped together in the call log are assigned the same + // day group. + currentGroupDayGroup = getDayGroup(date, currentTime); + // Create a group for the previous set of calls, excluding the current one, but do // not create a group for a single call. if (currentGroupSize > 1) { @@ -99,6 +171,9 @@ public class CallLogGroupBuilder { firstNumber = currentNumber; firstCallType = callType; } + + // Save the day group associated with the current call. + mGroupCreator.setDayGroup(currentCallId, currentGroupDayGroup); } // If the last set of calls at the end of the call log was itself a group, create it now. if (currentGroupSize > 1) { @@ -154,4 +229,25 @@ public class CallLogGroupBuilder { return userinfo1.equals(userinfo2) && rest1.equalsIgnoreCase(rest2); } + + /** + * Given a call date and the current date, determine which date group the call belongs in. + * + * @param date The call date. + * @param now The current date. + * @return The date group the call belongs in. + */ + private int getDayGroup(long date, long now) { + int days = DateUtils.getDayDifference(TIME, date, now); + + if (days == 0) { + return DAY_GROUP_TODAY; + } else if (days == 1) { + return DAY_GROUP_YESTERDAY; + } else if (days > 1 && days <=7) { + return DAY_GROUP_LAST_WEEK; + } else { + return DAY_GROUP_OTHER; + } + } } diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java index 333769d7e..648362e09 100644 --- a/src/com/android/dialer/calllog/CallLogListItemViews.java +++ b/src/com/android/dialer/calllog/CallLogListItemViews.java @@ -36,8 +36,10 @@ public final class CallLogListItemViews { public final View primaryActionView; /** The details of the phone call. */ public final PhoneCallDetailsViews phoneCallDetailsViews; - /** The text of the header of a section. */ - public final TextView listHeaderTextView; + /** The text of the header for a day grouping. */ + public final TextView dayGroupHeader; + /** The view containing the details for the call log row, including the action buttons. */ + public final View callLogEntryView; /** The view containing call log item actions. Null until the ViewStub is inflated. */ public View actionsView; /** The "call back" action button - assigned only when the action section is expanded. */ @@ -91,12 +93,13 @@ public final class CallLogListItemViews { public CharSequence nameOrNumber; private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView, - PhoneCallDetailsViews phoneCallDetailsViews, - TextView listHeaderTextView) { + PhoneCallDetailsViews phoneCallDetailsViews, View callLogEntryView, + TextView dayGroupHeader) { this.quickContactView = quickContactView; this.primaryActionView = primaryActionView; this.phoneCallDetailsViews = phoneCallDetailsViews; - this.listHeaderTextView = listHeaderTextView; + this.callLogEntryView = callLogEntryView; + this.dayGroupHeader = dayGroupHeader; } public static CallLogListItemViews fromView(View view) { @@ -104,7 +107,8 @@ public final class CallLogListItemViews { (QuickContactBadge) view.findViewById(R.id.quick_contact_photo), view.findViewById(R.id.primary_action_view), PhoneCallDetailsViews.fromView(view), - (TextView) view.findViewById(R.id.call_log_header)); + view.findViewById(R.id.call_log_row), + (TextView) view.findViewById(R.id.call_log_day_group_label)); } @NeededForTesting @@ -113,6 +117,7 @@ public final class CallLogListItemViews { new QuickContactBadge(context), new View(context), PhoneCallDetailsViews.createForTest(context), + new View(context), new TextView(context)); views.callBackButtonView = new TextView(context); views.deleteButtonView = new TextView(context); |