Open groups of calls in the CallDetailActivity.
This change allow showing the details of a group of calls in the
CallDetailActivity.
First of all, it adds a way to pass to the activity a list of calls,
namely by using an extra containing a list of ids of rows in the call
log. It preserves the existing mean to open the call details using a URI
for backward compatibility.
In turns, the activity will get the details of each call, assuming they
all come from the same number and therefore the same person, and show a
single contact header at the top, while placing the detail of each of
the calls in the list at the bottom of the activity.
With that done, we no longer allow opening up groups of calls in the
call log: they are no longer necessary, as the details of the calls
within a group are now accessible in the details page.
In the process, extract a utility class that encapsulates operations on
phone number, like whether calls can be placed to a phone number, and
how to display the number to the user. There might be a few more places
where this helper can be used, but I will look at those in a follow-up.
A few optimizations still need to be pursued, for instance the lookup of
the details of the phone calls in a group can be optimized.
Another candidate for a follow-up is using a common ContactInfo class
between the call log and the call details, maybe with shared functions
to extra the information from a cursor.
A further improvement would be to have a shared cache of the contact
info and call log details, so that looking up the information when
opening the call details can be done more efficiently (it is currently a
strict mode violation) since the call details will generally require
information which had already been looked up by the call log beforehand.
Change-Id: I408650d7485afe4c132dd9cc376e6ba0e3388c1e
diff --git a/res/layout/call_log_list_group_item.xml b/res/layout/call_log_list_group_item.xml
index 57465a6..0eaeaa6 100644
--- a/res/layout/call_log_list_group_item.xml
+++ b/res/layout/call_log_list_group_item.xml
@@ -18,8 +18,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/call_log_list_item_height"
>
-
<include layout="@layout/call_log_contact_photo"/>
- <include layout="@layout/call_log_action_group"/>
+ <include layout="@layout/call_log_action_call"/>
<include layout="@layout/call_log_list_item_layout" />
</RelativeLayout>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index ca17e7b..bf5d8aa 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -18,7 +18,7 @@
import com.android.contacts.calllog.CallDetailHistoryAdapter;
import com.android.contacts.calllog.CallTypeHelper;
-import com.android.internal.telephony.CallerInfo;
+import com.android.contacts.calllog.PhoneNumberHelper;
import android.app.ListActivity;
import android.content.ContentResolver;
@@ -37,6 +37,7 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -53,14 +54,21 @@
/**
* Displays the details of a specific call log entry.
+ * <p>
+ * This activity can be either started with the URI of a single call log entry, or with the
+ * {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log entries.
*/
public class CallDetailActivity extends ListActivity implements
AdapterView.OnItemClickListener {
private static final String TAG = "CallDetail";
+ /** A long array extra containing ids of call log entries to display. */
+ public static final String EXTRA_CALL_LOG_IDS = "com.android.contacts.CALL_LOG_IDS";
+
/** The views representing the details of a phone call. */
private PhoneCallDetailsViews mPhoneCallDetailsViews;
private CallTypeHelper mCallTypeHelper;
+ private PhoneNumberHelper mPhoneNumberHelper;
private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
private View mHomeActionView;
private ImageView mMainActionView;
@@ -120,8 +128,9 @@
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));
- mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(this, getResources(),
- getVoicemailNumber(), mCallTypeHelper);
+ mPhoneNumberHelper = new PhoneNumberHelper(mResources, getVoicemailNumber());
+ mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(this, mResources, mCallTypeHelper,
+ mPhoneNumberHelper);
mHomeActionView = findViewById(R.id.action_bar_home);
mMainActionView = (ImageView) findViewById(R.id.main_action);
mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
@@ -141,7 +150,29 @@
@Override
public void onResume() {
super.onResume();
- updateData(getIntent().getData());
+ updateData(getCallLogEntryUris());
+ }
+
+ /**
+ * Returns the list of URIs to show.
+ * <p>
+ * There are two ways the URIs can be provided to the activity: as the data on the intent, or as
+ * a list of ids in the call log added as an extra on the URI.
+ * <p>
+ * If both are available, the data on the intent takes precedence.
+ */
+ private Uri[] getCallLogEntryUris() {
+ Uri uri = getIntent().getData();
+ if (uri != null) {
+ // If there is a data on the intent, it takes precedence over the extra.
+ return new Uri[]{ uri };
+ }
+ long[] ids = getIntent().getLongArrayExtra(EXTRA_CALL_LOG_IDS);
+ Uri[] uris = new Uri[ids.length];
+ for (int index = 0; index < ids.length; ++index) {
+ uris[index] = ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, ids[index]);
+ }
+ return uris;
}
@Override
@@ -166,146 +197,178 @@
/**
* Update user interface with details of given call.
*
- * @param callUri Uri into {@link CallLog.Calls}
+ * @param callUris URIs into {@link CallLog.Calls} of the calls to be displayed
*/
- private void updateData(final Uri callUri) {
+ private void updateData(final Uri... callUris) {
+ // TODO: All phone calls correspond to the same person, so we can make a single lookup.
+ final int numCalls = callUris.length;
+ final PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
+ try {
+ for (int index = 0; index < numCalls; ++index) {
+ details[index] = getPhoneCallDetailsForUri(callUris[index]);
+ }
+ } catch (IllegalArgumentException e) {
+ // Something went wrong reading in our primary data, so we're going to
+ // bail out and show error to users.
+ Log.w(TAG, "invalid URI starting call details", e);
+ Toast.makeText(this, R.string.toast_call_detail_error,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ // We know that all calls are from the same number and the same contact, so pick the first.
+ mNumber = details[0].number.toString();
+ final long personId = details[0].personId;
+ final long photoId = details[0].photoId;
+
+ // Set the details header, based on the first phone call.
+ mPhoneCallDetailsHelper.setPhoneCallDetails(mPhoneCallDetailsViews,
+ details[0], false);
+
+ // Let user view contact details if they exist, otherwise add option to create new contact
+ // from this number.
+ final Intent mainActionIntent;
+ final int mainActionIcon;
+
+ if (details[0].personId != -1) {
+ Uri personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, personId);
+ mainActionIntent = new Intent(Intent.ACTION_VIEW, personUri);
+ mainActionIcon = R.drawable.sym_action_view_contact;
+ } else {
+ mainActionIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ mainActionIntent.setType(Contacts.CONTENT_ITEM_TYPE);
+ mainActionIntent.putExtra(Insert.PHONE, mNumber);
+ mainActionIcon = R.drawable.sym_action_add;
+ }
+
+ mMainActionView.setVisibility(View.VISIBLE);
+ mMainActionView.setImageResource(mainActionIcon);
+ mMainActionView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(mainActionIntent);
+ }
+ });
+
+ // Build list of various available actions.
+ final List<ViewEntry> actions = new ArrayList<ViewEntry>();
+
+ final boolean isSipNumber = PhoneNumberUtils.isUriNumber(mNumber);
+ final Uri numberCallUri = mPhoneNumberHelper.getCallUri(mNumber);
+
+ // This action allows to call the number that places the call.
+ if (mPhoneNumberHelper.canPlaceCallsTo(mNumber)) {
+ actions.add(new ViewEntry(android.R.drawable.sym_action_call,
+ getString(R.string.menu_callNumber, mNumber),
+ new Intent(Intent.ACTION_CALL_PRIVILEGED, numberCallUri)));
+ }
+
+ if (!isSipNumber) {
+ // SMS is only available for PSTN numbers.
+ Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts("sms", mNumber, null));
+ actions.add(new ViewEntry(R.drawable.sym_action_sms,
+ getString(R.string.menu_sendTextMessage), smsIntent));
+ }
+
+ // This action deletes all elements in the group from the call log.
+ actions.add(new ViewEntry(android.R.drawable.ic_menu_close_clear_cancel,
+ getString(R.string.recentCalls_removeFromRecentList),
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ StringBuilder callIds = new StringBuilder();
+ for (Uri callUri : callUris) {
+ if (callIds.length() != 0) {
+ callIds.append(",");
+ }
+ callIds.append(ContentUris.parseId(callUri));
+ }
+
+ getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
+ Calls._ID + " IN (" + callIds + ")", null);
+ finish();
+ }
+ }));
+
+ if (!isSipNumber) {
+ // "Edit the number before calling" is only available for PSTN numbers.
+ actions.add(new ViewEntry(android.R.drawable.sym_action_call,
+ getString(R.string.recentCalls_editNumberBeforeCall),
+ new Intent(Intent.ACTION_DIAL, numberCallUri)));
+ }
+
+ // Set the actions for this phone number.
+ setListAdapter(new ViewAdapter(this, actions));
+
+ ListView historyList = (ListView) findViewById(R.id.history);
+ historyList.setAdapter(
+ new CallDetailHistoryAdapter(this, mInflater, mCallTypeHelper, details));
+ loadContactPhotos(photoId);
+ }
+
+ /** Return the phone call details for a given call log URI. */
+ private PhoneCallDetails getPhoneCallDetailsForUri(Uri callUri) {
ContentResolver resolver = getContentResolver();
Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
try {
- if (callCursor != null && callCursor.moveToFirst()) {
- // Read call log specifics
- mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
- long date = callCursor.getLong(DATE_COLUMN_INDEX);
- long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
- int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
- String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
- if (TextUtils.isEmpty(countryIso)) {
- countryIso = mDefaultCountryIso;
- }
-
- long photoId = 0L;
- CharSequence nameText = "";
- final CharSequence numberText;
- int numberType = 0;
- CharSequence numberLabel = "";
- if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
- mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
- numberText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
- ? R.string.private_num : R.string.unknown);
- mMainActionView.setVisibility(View.GONE);
- } else {
- // Perform a reverse-phonebook lookup to find the PERSON_ID
- Uri personUri = null;
- Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(mNumber));
- Cursor phonesCursor = resolver.query(
- phoneUri, PHONES_PROJECTION, null, null, null);
- String candidateNumberText = mNumber;
- try {
- if (phonesCursor != null && phonesCursor.moveToFirst()) {
- long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
- personUri = ContentUris.withAppendedId(
- Contacts.CONTENT_URI, personId);
- nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
- photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
- candidateNumberText = PhoneNumberUtils.formatNumber(
- phonesCursor.getString(COLUMN_INDEX_NUMBER),
- phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
- countryIso);
- numberType = phonesCursor.getInt(COLUMN_INDEX_TYPE);
- numberLabel = phonesCursor.getString(COLUMN_INDEX_LABEL);
- } else {
- candidateNumberText =
- PhoneNumberUtils.formatNumber(mNumber, countryIso);
- }
- } finally {
- if (phonesCursor != null) phonesCursor.close();
- numberText = candidateNumberText;
- }
-
- // Let user view contact details if they exist, otherwise add option
- // to create new contact from this number.
- final Intent mainActionIntent;
- final int mainActionIcon;
- if (personUri != null) {
- mainActionIntent = new Intent(Intent.ACTION_VIEW, personUri);
- mainActionIcon = R.drawable.sym_action_view_contact;
- } else {
- mainActionIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- mainActionIntent.setType(Contacts.CONTENT_ITEM_TYPE);
- mainActionIntent.putExtra(Insert.PHONE, mNumber);
- mainActionIcon = R.drawable.sym_action_add;
- }
-
- mMainActionView.setVisibility(View.VISIBLE);
- mMainActionView.setImageResource(mainActionIcon);
- mMainActionView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivity(mainActionIntent);
- }
- });
-
- // Build list of various available actions
- List<ViewEntry> actions = new ArrayList<ViewEntry>();
-
- final boolean isSipNumber = PhoneNumberUtils.isUriNumber(mNumber);
- final Uri numberCallUri;
- if (isSipNumber) {
- numberCallUri = Uri.fromParts("sip", mNumber, null);
- } else {
- numberCallUri = Uri.fromParts("tel", mNumber, null);
- }
-
- actions.add(new ViewEntry(android.R.drawable.sym_action_call,
- getString(R.string.menu_callNumber, mNumber),
- new Intent(Intent.ACTION_CALL_PRIVILEGED, numberCallUri)));
-
- if (!isSipNumber) {
- Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts("sms", mNumber, null));
- actions.add(new ViewEntry(R.drawable.sym_action_sms,
- getString(R.string.menu_sendTextMessage), smsIntent));
- }
-
- actions.add(new ViewEntry(android.R.drawable.ic_menu_close_clear_cancel,
- getString(R.string.recentCalls_removeFromRecentList),
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- long id = ContentUris.parseId(callUri);
- getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
- Calls._ID + " = ?", new String[]{Long.toString(id)});
- finish();
- }
- }));
-
- if (!isSipNumber) {
- actions.add(new ViewEntry(android.R.drawable.sym_action_call,
- getString(R.string.recentCalls_editNumberBeforeCall),
- new Intent(Intent.ACTION_DIAL, numberCallUri)));
- }
-
- ViewAdapter adapter = new ViewAdapter(this, actions);
- setListAdapter(adapter);
- }
- PhoneCallDetails details = new PhoneCallDetails(mNumber, numberText,
- new int[]{ callType }, duration, date, nameText, numberType, numberLabel);
- mPhoneCallDetailsHelper.setPhoneCallDetails(mPhoneCallDetailsViews,
- details, false);
- ListView historyList = (ListView) findViewById(R.id.history);
- historyList.setAdapter(
- new CallDetailHistoryAdapter(this, mInflater, mCallTypeHelper,
- new PhoneCallDetails[]{ details }));
-
- loadContactPhotos(photoId);
- } else {
- // Something went wrong reading in our primary data, so we're going to
- // bail out and show error to users.
- Toast.makeText(this, R.string.toast_call_detail_error,
- Toast.LENGTH_SHORT).show();
- finish();
+ if (callCursor == null || !callCursor.moveToFirst()) {
+ throw new IllegalArgumentException("Cannot find content: " + callUri);
}
+
+ // Read call log specifics.
+ String number = callCursor.getString(NUMBER_COLUMN_INDEX);
+ long date = callCursor.getLong(DATE_COLUMN_INDEX);
+ long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
+ int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
+ String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
+ if (TextUtils.isEmpty(countryIso)) {
+ countryIso = mDefaultCountryIso;
+ }
+
+ // Formatted phone number.
+ final CharSequence numberText;
+ // Read contact specifics.
+ CharSequence nameText = "";
+ int numberType = 0;
+ CharSequence numberLabel = "";
+ long personId = -1L;
+ long photoId = 0L;
+ // If this is not a regular number, there is no point in looking it up in the contacts.
+ if (!mPhoneNumberHelper.canPlaceCallsTo(number)) {
+ numberText = mPhoneNumberHelper.getDisplayNumber(number, null);
+ } else {
+ // Perform a reverse-phonebook lookup to find the contact details.
+ Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(number));
+ Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
+ String candidateNumberText = number;
+ try {
+ if (phonesCursor != null && phonesCursor.moveToFirst()) {
+ personId = phonesCursor.getLong(COLUMN_INDEX_ID);
+ nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
+ photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
+ candidateNumberText = PhoneNumberUtils.formatNumber(
+ phonesCursor.getString(COLUMN_INDEX_NUMBER),
+ phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
+ countryIso);
+ numberType = phonesCursor.getInt(COLUMN_INDEX_TYPE);
+ numberLabel = phonesCursor.getString(COLUMN_INDEX_LABEL);
+ } else {
+ // We could not find this contact in the contacts, just format the phone
+ // number as best as we can. All the other fields will have their default
+ // values.
+ candidateNumberText =
+ PhoneNumberUtils.formatNumber(number, countryIso);
+ }
+ } finally {
+ if (phonesCursor != null) phonesCursor.close();
+ numberText = candidateNumberText;
+ }
+ }
+ return new PhoneCallDetails(number, numberText, new int[]{ callType }, date, duration,
+ nameText, numberType, numberLabel, personId, photoId);
} finally {
if (callCursor != null) {
callCursor.close();
diff --git a/src/com/android/contacts/PhoneCallDetails.java b/src/com/android/contacts/PhoneCallDetails.java
index 39620b2..6ab47aa 100644
--- a/src/com/android/contacts/PhoneCallDetails.java
+++ b/src/com/android/contacts/PhoneCallDetails.java
@@ -43,16 +43,21 @@
public final int numberType;
/** The custom label associated with the phone number in the contact, or the empty string. */
public final CharSequence numberLabel;
+ /** The id of the contact associated with this phone call. */
+ public final long personId;
+ /** The photo id of the contact associated with this phone call. */
+ public final long photoId;
/** Create the details for a call with a number not associated with a contact. */
public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
long date, long duration) {
- this(number, formattedNumber, callTypes, date, duration, "", 0, "");
+ this(number, formattedNumber, callTypes, date, duration, "", 0, "", -1L, 0L);
}
/** Create the details for a call with a number associated with a contact. */
public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
- long date, long duration, CharSequence name, int numberType, CharSequence numberLabel) {
+ long date, long duration, CharSequence name, int numberType, CharSequence numberLabel,
+ long personId, long photoId) {
this.number = number;
this.formattedNumber = formattedNumber;
this.callTypes = callTypes;
@@ -61,5 +66,7 @@
this.name = name;
this.numberType = numberType;
this.numberLabel = numberLabel;
+ this.personId = personId;
+ this.photoId = photoId;
}
}
diff --git a/src/com/android/contacts/PhoneCallDetailsHelper.java b/src/com/android/contacts/PhoneCallDetailsHelper.java
index cbf3c5c..6bdfbaa 100644
--- a/src/com/android/contacts/PhoneCallDetailsHelper.java
+++ b/src/com/android/contacts/PhoneCallDetailsHelper.java
@@ -17,8 +17,8 @@
package com.android.contacts;
import com.android.contacts.calllog.CallTypeHelper;
+import com.android.contacts.calllog.PhoneNumberHelper;
import com.android.contacts.format.FormatUtils;
-import com.android.internal.telephony.CallerInfo;
import android.content.Context;
import android.content.res.Resources;
@@ -37,10 +37,11 @@
public class PhoneCallDetailsHelper {
private final Context mContext;
private final Resources mResources;
- private final String mVoicemailNumber;
/** The injected current time in milliseconds since the epoch. Used only by tests. */
private Long mCurrentTimeMillisForTest;
+ // Helper classes.
private final CallTypeHelper mCallTypeHelper;
+ private final PhoneNumberHelper mPhoneNumberHelper;
/**
* Creates a new instance of the helper.
@@ -49,12 +50,12 @@
*
* @param resources used to look up strings
*/
- public PhoneCallDetailsHelper(Context context, Resources resources, String voicemailNumber,
- CallTypeHelper callTypeHelper) {
+ public PhoneCallDetailsHelper(Context context, Resources resources,
+ CallTypeHelper callTypeHelper, PhoneNumberHelper phoneNumberHelper) {
mContext = context;
mResources = resources;
- mVoicemailNumber = voicemailNumber;
mCallTypeHelper = callTypeHelper;
+ mPhoneNumberHelper = phoneNumberHelper;
}
/** Fills the call details views with content. */
@@ -100,12 +101,13 @@
final CharSequence nameText;
final CharSequence numberText;
+ final CharSequence displayNumber =
+ mPhoneNumberHelper.getDisplayNumber(details.number, details.formattedNumber);
if (TextUtils.isEmpty(details.name)) {
- nameText = getDisplayNumber(details.number, details.formattedNumber);
+ nameText = displayNumber;
numberText = "";
} else {
nameText = details.name;
- CharSequence displayNumber = getDisplayNumber(details.number, details.formattedNumber);
if (numberFormattedLabel != null) {
numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
numberFormattedLabel + " " + displayNumber, 0,
@@ -130,29 +132,6 @@
}
}
- private CharSequence getDisplayNumber(CharSequence number, CharSequence formattedNumber) {
- if (TextUtils.isEmpty(number)) {
- return "";
- }
- if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
- return mResources.getString(R.string.unknown);
- }
- if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
- return mResources.getString(R.string.private_num);
- }
- if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
- return mResources.getString(R.string.payphone);
- }
- if (PhoneNumberUtils.extractNetworkPortion(number.toString()).equals(mVoicemailNumber)) {
- return mResources.getString(R.string.voicemail);
- }
- if (TextUtils.isEmpty(formattedNumber)) {
- return number;
- } else {
- return formattedNumber;
- }
- }
-
public void setCurrentTimeForTest(long currentTimeMillis) {
mCurrentTimeMillisForTest = currentTimeMillis;
}
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 901ebd8..b5d665a 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -81,9 +81,7 @@
implements View.OnCreateContextMenuListener, ViewPagerVisibilityListener {
private static final String TAG = "CallLogFragment";
- /**
- * The size of the cache of contact info.
- */
+ /** The size of the cache of contact info. */
private static final int CONTACT_INFO_CACHE_SIZE = 100;
/** The query for the call log table */
@@ -278,10 +276,12 @@
mContactPhotoManager = ContactPhotoManager.getInstance(getActivity());
CallTypeHelper callTypeHelper = new CallTypeHelper(resources, incomingDrawable,
outgoingDrawable, missedDrawable, voicemailDrawable);
+ PhoneNumberHelper phoneNumberHelper =
+ new PhoneNumberHelper(getResources(), mVoiceMailNumber);
PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
- getActivity(), resources, mVoiceMailNumber, callTypeHelper);
- mCallLogViewsHelper = new CallLogListItemHelper(phoneCallDetailsHelper, callDrawable,
- playDrawable);
+ getActivity(), resources, callTypeHelper, phoneNumberHelper );
+ mCallLogViewsHelper = new CallLogListItemHelper(phoneCallDetailsHelper,
+ phoneNumberHelper, callDrawable, playDrawable);
}
/**
@@ -687,7 +687,7 @@
formattedNumber = formatPhoneNumber(number, null, countryIso);
}
- long contactId = info.personId;
+ long personId = info.personId;
String name = info.name;
int ntype = info.type;
String label = info.label;
@@ -705,11 +705,11 @@
details = new PhoneCallDetails(number, formattedNumber, callTypes, date, duration);
} else {
details = new PhoneCallDetails(number, formattedNumber, callTypes, date, duration,
- name, ntype, label);
+ name, ntype, label, personId, photoId);
}
mCallLogViewsHelper.setPhoneCallDetails(views, details , true);
if (views.photoView != null) {
- bindQuickContact(views.photoView, photoId, contactId, lookupKey);
+ bindQuickContact(views.photoView, photoId, personId, lookupKey);
}
@@ -1162,13 +1162,25 @@
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
+ Intent intent = new Intent(getActivity(), CallDetailActivity.class);
if (mAdapter.isGroupHeader(position)) {
- mAdapter.toggleGroup(position);
+ int groupSize = mAdapter.getGroupSize(position);
+ long[] ids = new long[groupSize];
+ // Copy the ids of the rows in the group.
+ Cursor cursor = (Cursor) mAdapter.getItem(position);
+ // Restore the position in the cursor at the end.
+ int currentPosition = cursor.getPosition();
+ for (int index = 0; index < groupSize; ++index) {
+ ids[index] = cursor.getLong(CallLogQuery.ID);
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(currentPosition);
+ intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, ids);
} else {
- Intent intent = new Intent(getActivity(), CallDetailActivity.class);
+ // If there is a single item, use the direct URI for it.
intent.setData(ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, id));
- startActivity(intent);
}
+ startActivity(intent);
}
@VisibleForTesting
diff --git a/src/com/android/contacts/calllog/CallLogListItemHelper.java b/src/com/android/contacts/calllog/CallLogListItemHelper.java
index e4630e9..a8894da 100644
--- a/src/com/android/contacts/calllog/CallLogListItemHelper.java
+++ b/src/com/android/contacts/calllog/CallLogListItemHelper.java
@@ -18,11 +18,9 @@
import com.android.contacts.PhoneCallDetails;
import com.android.contacts.PhoneCallDetailsHelper;
-import com.android.internal.telephony.CallerInfo;
import android.graphics.drawable.Drawable;
import android.provider.CallLog.Calls;
-import android.text.TextUtils;
import android.view.View;
/**
@@ -31,6 +29,8 @@
/*package*/ class CallLogListItemHelper {
/** Helper for populating the details of a phone call. */
private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
+ /** Helper for handling phone numbers. */
+ private final PhoneNumberHelper mPhoneNumberHelper;
/** Icon for the call action. */
private final Drawable mCallDrawable;
/** Icon for the play action. */
@@ -44,8 +44,9 @@
* @param playDrawable used to render the play button, for playing a voicemail
*/
public CallLogListItemHelper(PhoneCallDetailsHelper phoneCallDetailsHelper,
- Drawable callDrawable, Drawable playDrawable) {
+ PhoneNumberHelper phoneNumberHelper, Drawable callDrawable, Drawable playDrawable) {
mPhoneCallDetailsHelper = phoneCallDetailsHelper;
+ mPhoneNumberHelper= phoneNumberHelper;
mCallDrawable = callDrawable;
mPlayDrawable = playDrawable;
}
@@ -65,15 +66,8 @@
views.callView.setImageDrawable(
details.callTypes[0] == Calls.VOICEMAIL_TYPE ? mPlayDrawable : mCallDrawable);
views.callView.setVisibility(
- canPlaceCallsTo(details.number) ? View.VISIBLE : View.INVISIBLE);
+ mPhoneNumberHelper.canPlaceCallsTo(details.number)
+ ? View.VISIBLE : View.INVISIBLE);
}
}
-
- /** Returns true if it is possible to place a call to the given number. */
- public boolean canPlaceCallsTo(CharSequence number) {
- return !(TextUtils.isEmpty(number)
- || number.equals(CallerInfo.UNKNOWN_NUMBER)
- || number.equals(CallerInfo.PRIVATE_NUMBER)
- || number.equals(CallerInfo.PAYPHONE_NUMBER));
- }
}
diff --git a/src/com/android/contacts/calllog/PhoneNumberHelper.java b/src/com/android/contacts/calllog/PhoneNumberHelper.java
new file mode 100644
index 0000000..b30fed5
--- /dev/null
+++ b/src/com/android/contacts/calllog/PhoneNumberHelper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.calllog;
+
+import com.android.contacts.R;
+import com.android.internal.telephony.CallerInfo;
+
+import android.content.res.Resources;
+import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+
+/**
+ * Helper for formatting and managing phone numbers.
+ */
+public class PhoneNumberHelper {
+ private final Resources mResources;
+ private final String mVoicemailNumber;
+
+ public PhoneNumberHelper(Resources resources, String voicemailNumber) {
+ mResources = resources;
+ mVoicemailNumber = voicemailNumber;
+ }
+
+ /** Returns true if it is possible to place a call to the given number. */
+ public boolean canPlaceCallsTo(CharSequence number) {
+ return !(TextUtils.isEmpty(number)
+ || number.equals(CallerInfo.UNKNOWN_NUMBER)
+ || number.equals(CallerInfo.PRIVATE_NUMBER)
+ || number.equals(CallerInfo.PAYPHONE_NUMBER));
+ }
+
+ /**
+ * Returns the string to display for the given phone number.
+ *
+ * @param number the number to display
+ * @param formattedNumber the formatted number if available, may be null
+ */
+ public CharSequence getDisplayNumber(CharSequence number, CharSequence formattedNumber) {
+ if (TextUtils.isEmpty(number)) {
+ return "";
+ }
+ if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
+ return mResources.getString(R.string.unknown);
+ }
+ if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
+ return mResources.getString(R.string.private_num);
+ }
+ if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
+ return mResources.getString(R.string.payphone);
+ }
+ if (PhoneNumberUtils.extractNetworkPortion(number.toString()).equals(mVoicemailNumber)) {
+ return mResources.getString(R.string.voicemail);
+ }
+ if (TextUtils.isEmpty(formattedNumber)) {
+ return number;
+ } else {
+ return formattedNumber;
+ }
+ }
+
+ /** Returns a URI that can be used to place a call to this number. */
+ public Uri getCallUri(String number) {
+ if (PhoneNumberUtils.isUriNumber(number)) {
+ return Uri.fromParts("sip", number, null);
+ } else {
+ return Uri.fromParts("tel", number, null);
+ }
+ }
+}
diff --git a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
index 9ac3b05..4f95563 100644
--- a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
@@ -17,6 +17,7 @@
package com.android.contacts;
import com.android.contacts.calllog.CallTypeHelper;
+import com.android.contacts.calllog.PhoneNumberHelper;
import com.android.contacts.util.LocaleTestUtils;
import com.android.internal.telephony.CallerInfo;
@@ -70,8 +71,9 @@
Resources resources = context.getResources();
CallTypeHelper callTypeHelper = new CallTypeHelper(resources, TEST_INCOMING_DRAWABLE,
TEST_OUTGOING_DRAWABLE, TEST_MISSED_DRAWABLE, TEST_VOICEMAIL_DRAWABLE);
- mHelper = new PhoneCallDetailsHelper(context, resources,
- TEST_VOICEMAIL_NUMBER, callTypeHelper);
+ PhoneNumberHelper phoneNumberHelper =
+ new PhoneNumberHelper(resources, TEST_VOICEMAIL_NUMBER);
+ mHelper = new PhoneCallDetailsHelper(context, resources, callTypeHelper, phoneNumberHelper);
mViews = PhoneCallDetailsViews.createForTest(new TextView(context),
new LinearLayout(context), new TextView(context), new View(context),
new TextView(context), new TextView(context));
diff --git a/tests/src/com/android/contacts/activities/CallLogActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
index 8db291d..67e6196 100644
--- a/tests/src/com/android/contacts/activities/CallLogActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -166,8 +166,7 @@
insert(CallerInfo.PRIVATE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
View view = mAdapter.newGroupView(getActivity(), mParentView);
mAdapter.bindGroupView(view, getActivity(), mCursor, 3, false);
- assertNull(view.findViewById(R.id.call_icon));
- assertNotNull(view.findViewById(R.id.groupIndicator));
+ assertNotNull(view.findViewById(R.id.call_icon));
}
@MediumTest
diff --git a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
index fc50275..a9f4e79 100644
--- a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
@@ -75,10 +75,12 @@
CallTypeHelper callTypeHelper = new CallTypeHelper(resources,
TEST_INCOMING_DRAWABLE, TEST_OUTGOING_DRAWABLE, TEST_MISSED_DRAWABLE,
TEST_VOICEMAIL_DRAWABLE);
+ PhoneNumberHelper phoneNumberHelper =
+ new PhoneNumberHelper(resources, TEST_VOICEMAIL_NUMBER);
PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(context,
- resources, TEST_VOICEMAIL_NUMBER, callTypeHelper);
- mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, TEST_CALL_DRAWABLE,
- TEST_PLAY_DRAWABLE);
+ resources, callTypeHelper, phoneNumberHelper);
+ mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, phoneNumberHelper,
+ TEST_CALL_DRAWABLE, TEST_PLAY_DRAWABLE);
mViews = CallLogListItemViews.createForTest(new QuickContactBadge(context),
new ImageView(context), PhoneCallDetailsViews.createForTest(new TextView(context),
new LinearLayout(context), new TextView(context), new TextView(context),