summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/dialer/DialerApplication.java14
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java313
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java4
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViews.java125
-rw-r--r--src/com/android/dialer/calllog/ContactInfoHelper.java130
-rw-r--r--src/com/android/dialer/contactinfo/ContactInfoRequest.java65
-rw-r--r--src/com/android/dialer/contactinfo/NumberWithCountryIso.java53
7 files changed, 393 insertions, 311 deletions
diff --git a/src/com/android/dialer/DialerApplication.java b/src/com/android/dialer/DialerApplication.java
index 45457c648..7bc3bb4d3 100644
--- a/src/com/android/dialer/DialerApplication.java
+++ b/src/com/android/dialer/DialerApplication.java
@@ -40,18 +40,4 @@ public class DialerApplication extends Application {
Trace.endSection();
Trace.endSection();
}
-
- @Override
- public Object getSystemService(String name) {
- if (ContactPhotoManager.CONTACT_PHOTO_SERVICE.equals(name)) {
- if (mContactPhotoManager == null) {
- mContactPhotoManager = ContactPhotoManager.createContactPhotoManager(this);
- registerComponentCallbacks(mContactPhotoManager);
- mContactPhotoManager.preloadPhotosInBackground();
- }
- return mContactPhotoManager;
- }
-
- return super.getSystemService(name);
- }
}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 173306882..8ea861fb3 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -16,16 +16,13 @@
package com.android.dialer.calllog;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
-import android.database.sqlite.SQLiteFullException;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
-import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.PhoneLookup;
import android.telecom.PhoneAccountHandle;
@@ -42,17 +39,16 @@ import android.widget.ImageView;
import android.widget.TextView;
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.UriUtils;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.PhoneCallDetailsHelper;
import com.android.dialer.R;
+import com.android.dialer.contactinfo.ContactInfoRequest;
+import com.android.dialer.contactinfo.NumberWithCountryIso;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.ExpirableCache;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
import java.util.HashMap;
import java.util.LinkedList;
@@ -64,8 +60,6 @@ public class CallLogAdapter extends GroupingListAdapter
implements ViewTreeObserver.OnPreDrawListener, CallLogGroupBuilder.GroupCreator {
private static final String TAG = CallLogAdapter.class.getSimpleName();
- private static final int VOICEMAIL_TRANSCRIPTION_MAX_LINES = 10;
-
/** The enumeration of {@link android.os.AsyncTask} objects used in this class. */
public enum Tasks {
REMOVE_CALL_LOG_ENTRIES,
@@ -99,37 +93,6 @@ public class CallLogAdapter extends GroupingListAdapter
public void onReportButtonClick(String number);
}
- /**
- * Stores a phone number of a call with the country code where it originally occurred.
- * <p>
- * Note the country does not necessarily specifies the country of the phone number itself, but
- * it is the country in which the user was in when the call was placed or received.
- */
- private static final class NumberWithCountryIso {
- public final String number;
- public final String countryIso;
-
- public NumberWithCountryIso(String number, String countryIso) {
- this.number = number;
- this.countryIso = countryIso;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) return false;
- if (!(o instanceof NumberWithCountryIso)) return false;
- NumberWithCountryIso other = (NumberWithCountryIso) o;
- return TextUtils.equals(number, other.number)
- && TextUtils.equals(countryIso, other.countryIso);
- }
-
- @Override
- public int hashCode() {
- return (number == null ? 0 : number.hashCode())
- ^ (countryIso == null ? 0 : countryIso.hashCode());
- }
- }
-
/** The time in millis to delay starting the thread processing requests. */
private static final int START_PROCESSING_REQUESTS_DELAY_MILLIS = 1000;
@@ -181,49 +144,6 @@ public class CallLogAdapter extends GroupingListAdapter
private HashMap<Long,Integer> mDayGroups = new HashMap<Long, Integer>();
/**
- * A request for contact details for the given number.
- */
- private static final class ContactInfoRequest {
- /** The number to look-up. */
- public final String number;
- /** The country in which a call to or from this number was placed or received. */
- public final String countryIso;
- /** The cached contact information stored in the call log. */
- public final ContactInfo callLogInfo;
-
- public ContactInfoRequest(String number, String countryIso, ContactInfo callLogInfo) {
- this.number = number;
- this.countryIso = countryIso;
- this.callLogInfo = callLogInfo;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (!(obj instanceof ContactInfoRequest)) return false;
-
- ContactInfoRequest other = (ContactInfoRequest) obj;
-
- if (!TextUtils.equals(number, other.number)) return false;
- if (!TextUtils.equals(countryIso, other.countryIso)) return false;
- if (!Objects.equal(callLogInfo, other.callLogInfo)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((callLogInfo == null) ? 0 : callLogInfo.hashCode());
- result = prime * result + ((countryIso == null) ? 0 : countryIso.hashCode());
- result = prime * result + ((number == null) ? 0 : number.hashCode());
- return result;
- }
- }
-
- /**
* List of requests to update contact details.
* <p>
* Each request is made of a phone number to look up, and the contact info currently stored in
@@ -243,8 +163,6 @@ public class CallLogAdapter extends GroupingListAdapter
/** Instance of helper class for managing views. */
private final CallLogListItemHelper mCallLogViewsHelper;
- /** Helper to set up contact photos. */
- private final ContactPhotoManager mContactPhotoManager;
/** Helper to parse and process phone numbers. */
private PhoneNumberDisplayHelper mPhoneNumberHelper;
/** Helper to access Telephony phone number utils class */
@@ -257,11 +175,6 @@ public class CallLogAdapter extends GroupingListAdapter
/** Can be set to true by tests to disable processing of requests. */
private volatile boolean mRequestProcessingDisabled = false;
- private int mCallLogBackgroundColor;
- private int mExpandedBackgroundColor;
- private float mExpandedTranslationZ;
- private int mPhotoSize;
-
/** Listener for the primary or secondary actions in the list.
* Primary opens the call details.
* Secondary calls or plays.
@@ -349,12 +262,7 @@ public class CallLogAdapter extends GroupingListAdapter
Resources resources = mContext.getResources();
CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
- mCallLogBackgroundColor = resources.getColor(R.color.background_dialer_list_items);
- mExpandedBackgroundColor = resources.getColor(R.color.call_log_expanded_background_color);
- mExpandedTranslationZ = resources.getDimension(R.dimen.call_log_expanded_translation_z);
- mPhotoSize = resources.getDimensionPixelSize(R.dimen.contact_photo_size);
- mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
mPhoneNumberHelper = new PhoneNumberDisplayHelper(mContext, resources);
mPhoneNumberUtilsWrapper = new PhoneNumberUtilsWrapper(mContext);
PhoneCallDetailsHelper phoneCallDetailsHelper =
@@ -494,9 +402,10 @@ public class CallLogAdapter extends GroupingListAdapter
// Store the data in the cache so that the UI thread can use to display it. Store it
// even if it has not changed so that it is marked as not expired.
mContactInfoCache.put(numberCountryIso, info);
+
// Update the call log even if the cache it is up-to-date: it is possible that the cache
// contains the value from a different call log entry.
- updateCallLogContactInfoCache(number, countryIso, info, callLogInfo);
+ mContactInfoHelper.updateCallLogContactInfo(number, countryIso, info, callLogInfo);
return updated;
}
@@ -656,7 +565,7 @@ public class CallLogAdapter extends GroupingListAdapter
// Stash away the Ids of the calls so that we can support deleting a row in the call log.
views.callIds = getCallIds(c, count);
- final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c);
+ final ContactInfo cachedContactInfo = mContactInfoHelper.getContactInfo(c);
final boolean isVoicemailNumber =
mPhoneNumberUtilsWrapper.isVoicemailNumber(accountHandle, number);
@@ -733,7 +642,12 @@ public class CallLogAdapter extends GroupingListAdapter
// Restore expansion state of the row on rebind. Inflate the actions ViewStub if required,
// and set its visibility state accordingly.
- expandOrCollapseActions(callLogItemView, isExpanded(rowId));
+ views.expandOrCollapseActions(
+ isExpanded(rowId),
+ mOnReportButtonClickListener,
+ mActionListener,
+ mPhoneNumberUtilsWrapper,
+ mCallLogViewsHelper);
if (TextUtils.isEmpty(name)) {
details = new PhoneCallDetails(number, numberPresentation, formattedNumber, countryIso,
@@ -747,17 +661,6 @@ public class CallLogAdapter extends GroupingListAdapter
mCallLogViewsHelper.setPhoneCallDetails(mContext, views, details);
- int contactType = ContactPhotoManager.TYPE_DEFAULT;
-
- if (isVoicemailNumber) {
- contactType = ContactPhotoManager.TYPE_VOICEMAIL;
- } else if (mContactInfoHelper.isBusiness(info.sourceType)) {
- contactType = ContactPhotoManager.TYPE_BUSINESS;
- }
-
- String lookupKey = lookupUri == null ? null
- : ContactInfoHelper.getLookupKeyFromUri(lookupUri);
-
String nameForDefaultImage = null;
if (TextUtils.isEmpty(name)) {
nameForDefaultImage = mPhoneNumberHelper.getDisplayNumber(details.accountHandle,
@@ -766,11 +669,8 @@ public class CallLogAdapter extends GroupingListAdapter
nameForDefaultImage = name;
}
- if (photoId == 0 && photoUri != null) {
- setPhoto(views, photoUri, lookupUri, nameForDefaultImage, lookupKey, contactType);
- } else {
- setPhoto(views, photoId, lookupUri, nameForDefaultImage, lookupKey, contactType);
- }
+ views.setPhoto(photoId, photoUri, lookupUri, nameForDefaultImage, isVoicemailNumber,
+ mContactInfoHelper.isBusiness(info.sourceType));
views.quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
// Listen for the first draw
@@ -812,6 +712,7 @@ public class CallLogAdapter extends GroupingListAdapter
}
return CallLogGroupBuilder.DAY_GROUP_NONE;
}
+
/**
* Determines if a call log row with the given Id is expanded.
* @param rowId The row Id of the call.
@@ -845,54 +746,6 @@ public class CallLogAdapter extends GroupingListAdapter
}
}
- /**
- * Expands or collapses the view containing the CALLBACK/REDIAL, VOICEMAIL and DETAILS action
- * buttons.
- *
- * @param callLogItem The call log entry parent view.
- * @param isExpanded The new expansion state of the view.
- */
- private void expandOrCollapseActions(View callLogItem, boolean isExpanded) {
- final CallLogListItemViews views = (CallLogListItemViews) callLogItem.getTag();
-
- expandVoicemailTranscriptionView(views, isExpanded);
- if (isExpanded) {
- // Inflate the view stub if necessary, and wire up the event handlers.
- views.inflateActionViewStub(callLogItem, mOnReportButtonClickListener, mActionListener,
- mPhoneNumberUtilsWrapper, mCallLogViewsHelper);
-
- views.actionsView.setVisibility(View.VISIBLE);
- views.actionsView.setAlpha(1.0f);
- views.callLogEntryView.setBackgroundColor(mExpandedBackgroundColor);
- views.callLogEntryView.setTranslationZ(mExpandedTranslationZ);
- callLogItem.setTranslationZ(mExpandedTranslationZ); // WAR
- } else {
- // When recycling a view, it is possible the actionsView ViewStub was previously
- // inflated so we should hide it in this case.
- if (views.actionsView != null) {
- views.actionsView.setVisibility(View.GONE);
- }
-
- views.callLogEntryView.setBackgroundColor(mCallLogBackgroundColor);
- views.callLogEntryView.setTranslationZ(0);
- callLogItem.setTranslationZ(0); // WAR
- }
- }
-
- public static void expandVoicemailTranscriptionView(CallLogListItemViews views,
- boolean isExpanded) {
- if (views.callType != Calls.VOICEMAIL_TYPE) {
- return;
- }
-
- final TextView view = views.phoneCallDetailsViews.voicemailTranscriptionView;
- if (TextUtils.isEmpty(view.getText())) {
- return;
- }
- view.setMaxLines(isExpanded ? VOICEMAIL_TRANSCRIPTION_MAX_LINES : 1);
- view.setSingleLine(!isExpanded);
- }
-
/** Checks whether the contact info from the call log matches the one from the contacts db. */
private boolean callLogInfoMatches(ContactInfo callLogInfo, ContactInfo info) {
// The call log only contains a subset of the fields in the contacts db.
@@ -902,105 +755,6 @@ public class CallLogAdapter extends GroupingListAdapter
&& TextUtils.equals(callLogInfo.label, info.label);
}
- /** Stores the updated contact info in the call log if it is different from the current one. */
- private void updateCallLogContactInfoCache(String number, String countryIso,
- ContactInfo updatedInfo, ContactInfo callLogInfo) {
- final ContentValues values = new ContentValues();
- boolean needsUpdate = false;
-
- if (callLogInfo != null) {
- if (!TextUtils.equals(updatedInfo.name, callLogInfo.name)) {
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- needsUpdate = true;
- }
-
- if (updatedInfo.type != callLogInfo.type) {
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- needsUpdate = true;
- }
-
- if (!TextUtils.equals(updatedInfo.label, callLogInfo.label)) {
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- needsUpdate = true;
- }
- if (!UriUtils.areEqual(updatedInfo.lookupUri, callLogInfo.lookupUri)) {
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- needsUpdate = true;
- }
- // Only replace the normalized number if the new updated normalized number isn't empty.
- if (!TextUtils.isEmpty(updatedInfo.normalizedNumber) &&
- !TextUtils.equals(updatedInfo.normalizedNumber, callLogInfo.normalizedNumber)) {
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- needsUpdate = true;
- }
- if (!TextUtils.equals(updatedInfo.number, callLogInfo.number)) {
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- needsUpdate = true;
- }
- if (updatedInfo.photoId != callLogInfo.photoId) {
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- needsUpdate = true;
- }
- final Uri updatedPhotoUriContactsOnly =
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri);
- if (!UriUtils.areEqual(updatedPhotoUriContactsOnly, callLogInfo.photoUri)) {
- values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(
- updatedPhotoUriContactsOnly));
- needsUpdate = true;
- }
- if (!TextUtils.equals(updatedInfo.formattedNumber, callLogInfo.formattedNumber)) {
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
- } else {
- // No previous values, store all of them.
- values.put(Calls.CACHED_NAME, updatedInfo.name);
- values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
- values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
- values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
- values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
- values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
- values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
- values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(
- UriUtils.nullForNonContactsUri(updatedInfo.photoUri)));
- values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
- needsUpdate = true;
- }
-
- if (!needsUpdate) return;
-
- try {
- if (countryIso == null) {
- mContext.getContentResolver().update(Calls.CONTENT_URI_WITH_VOICEMAIL, values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
- new String[]{ number });
- } else {
- mContext.getContentResolver().update(Calls.CONTENT_URI_WITH_VOICEMAIL, values,
- Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
- new String[]{ number, countryIso });
- }
- } catch (SQLiteFullException e) {
- Log.e(TAG, "Unable to update contact info in call log db", e);
- }
- }
-
- /** Returns the contact information as stored in the call log. */
- private ContactInfo getContactInfoFromCallLog(Cursor c) {
- ContactInfo info = new ContactInfo();
- info.lookupUri = UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_LOOKUP_URI));
- info.name = c.getString(CallLogQuery.CACHED_NAME);
- info.type = c.getInt(CallLogQuery.CACHED_NUMBER_TYPE);
- info.label = c.getString(CallLogQuery.CACHED_NUMBER_LABEL);
- String matchedNumber = c.getString(CallLogQuery.CACHED_MATCHED_NUMBER);
- info.number = matchedNumber == null ? c.getString(CallLogQuery.NUMBER) : matchedNumber;
- info.normalizedNumber = c.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER);
- info.photoId = c.getLong(CallLogQuery.CACHED_PHOTO_ID);
- info.photoUri = UriUtils.nullForNonContactsUri(
- UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_PHOTO_URI)));
- info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
- return info;
- }
-
/**
* Returns the call types for the given number of items in the cursor.
* <p>
@@ -1038,26 +792,6 @@ public class CallLogAdapter extends GroupingListAdapter
return features;
}
- private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri,
- String displayName, String identifier, int contactType) {
- views.quickContactView.assignContactUri(contactUri);
- views.quickContactView.setOverlay(null);
- DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
- contactType, true /* isCircular */);
- mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */,
- true /* isCircular */, request);
- }
-
- private void setPhoto(CallLogListItemViews views, Uri photoUri, Uri contactUri,
- String displayName, String identifier, int contactType) {
- views.quickContactView.assignContactUri(contactUri);
- views.quickContactView.setOverlay(null);
- DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
- contactType, true /* isCircular */);
- mContactPhotoManager.loadPhoto(views.quickContactView, photoUri, mPhotoSize,
- false /* darkTheme */, true /* isCircular */, request);
- }
-
/**
* Bind a call log entry view for testing purposes. Also inflates the action view stub so
* unit tests can access the buttons contained within.
@@ -1070,7 +804,7 @@ public class CallLogAdapter extends GroupingListAdapter
void bindViewForTest(View view, Context context, Cursor cursor) {
bindStandAloneView(view, context, cursor);
CallLogListItemViews views = CallLogListItemViews.fromView(context, view);
- views.inflateActionViewStub(view, mOnReportButtonClickListener, mActionListener,
+ views.inflateActionViewStub(mOnReportButtonClickListener, mActionListener,
mPhoneNumberUtilsWrapper, mCallLogViewsHelper);
}
@@ -1172,7 +906,12 @@ public class CallLogAdapter extends GroupingListAdapter
boolean expanded = toggleExpansion(views.rowId);
// Trigger loading of the viewstub and visual expand or collapse.
- expandOrCollapseActions(view, expanded);
+ views.expandOrCollapseActions(
+ expanded,
+ mOnReportButtonClickListener,
+ mActionListener,
+ mPhoneNumberUtilsWrapper,
+ mCallLogViewsHelper);
// Animate the expansion or collapse.
if (mCallItemExpandedListener != null) {
@@ -1182,11 +921,15 @@ public class CallLogAdapter extends GroupingListAdapter
// Animate the collapse of the previous item if it is still visible on screen.
if (mPreviouslyExpanded != NONE_EXPANDED) {
- View previousItem = mCallItemExpandedListener.getViewForCallId(
- mPreviouslyExpanded);
+ View previousItem = mCallItemExpandedListener.getViewForCallId(mPreviouslyExpanded);
if (previousItem != null) {
- expandOrCollapseActions(previousItem, false);
+ ((CallLogListItemViews) previousItem.getTag()).expandOrCollapseActions(
+ false /* isExpanded */,
+ mOnReportButtonClickListener,
+ mActionListener,
+ mPhoneNumberUtilsWrapper,
+ mCallLogViewsHelper);
if (animate) {
mCallItemExpandedListener.onItemExpanded(previousItem);
}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index c4e453c33..7b5907c1f 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -556,7 +556,7 @@ public class CallLogFragment extends ListFragment
if (!isExpand) {
viewHolder.actionsView.setVisibility(View.VISIBLE);
}
- CallLogAdapter.expandVoicemailTranscriptionView(viewHolder, !isExpand);
+ viewHolder.expandVoicemailTranscriptionView(!isExpand);
// Set up the fade effect for the action buttons.
if (isExpand) {
@@ -625,7 +625,7 @@ public class CallLogFragment extends ListFragment
// is defaulting to the value (0) at the start of the expand animation.
viewHolder.actionsView.setAlpha(1);
}
- CallLogAdapter.expandVoicemailTranscriptionView(viewHolder, isExpand);
+ viewHolder.expandVoicemailTranscriptionView(isExpand);
}
});
diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java
index b9a76a877..9d11a3ab6 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViews.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViews.java
@@ -17,8 +17,11 @@
package com.android.dialer.calllog;
import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
import android.provider.CallLog.Calls;
import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
@@ -26,6 +29,8 @@ import android.widget.QuickContactBadge;
import android.widget.TextView;
import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.dialer.PhoneCallDetailsViews;
import com.android.dialer.R;
@@ -40,6 +45,8 @@ import com.android.dialer.R;
* if the call log list item is eventually represented as a UI component.
*/
public final class CallLogListItemViews {
+ /** The root view of the call log list item */
+ public final View rootView;
/** The quick contact badge for the contact. */
public final QuickContactBadge quickContactView;
/** The primary action view of the entry. */
@@ -123,10 +130,18 @@ public final class CallLogListItemViews {
*/
public boolean canBeReportedAsInvalid;
+ private static final int VOICEMAIL_TRANSCRIPTION_MAX_LINES = 10;
+
private Context mContext;
+ private int mPhotoSize;
+
+ private int mCallLogBackgroundColor;
+ private int mExpandedBackgroundColor;
+ private float mExpandedTranslationZ;
private CallLogListItemViews(
Context context,
+ View rootView,
QuickContactBadge quickContactView,
View primaryActionView,
PhoneCallDetailsViews phoneCallDetailsViews,
@@ -134,11 +149,29 @@ public final class CallLogListItemViews {
TextView dayGroupHeader) {
mContext = context;
+ this.rootView = rootView;
this.quickContactView = quickContactView;
this.primaryActionView = primaryActionView;
this.phoneCallDetailsViews = phoneCallDetailsViews;
this.callLogEntryView = callLogEntryView;
this.dayGroupHeader = dayGroupHeader;
+
+ Resources resources = mContext.getResources();
+ mCallLogBackgroundColor = resources.getColor(R.color.background_dialer_list_items);
+ mExpandedBackgroundColor = resources.getColor(R.color.call_log_expanded_background_color);
+ mExpandedTranslationZ = resources.getDimension(R.dimen.call_log_expanded_translation_z);
+ mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
+ }
+
+ public static CallLogListItemViews fromView(Context context, View view) {
+ return new CallLogListItemViews(
+ context,
+ view,
+ (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
+ view.findViewById(R.id.primary_action_view),
+ PhoneCallDetailsViews.fromView(view),
+ view.findViewById(R.id.call_log_row),
+ (TextView) view.findViewById(R.id.call_log_day_group_label));
}
/**
@@ -149,12 +182,11 @@ public final class CallLogListItemViews {
* @param callLogItem The call log list item view.
*/
public void inflateActionViewStub(
- final View callLogItem,
final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener,
View.OnClickListener actionListener,
PhoneNumberUtilsWrapper phoneNumberUtilsWrapper,
CallLogListItemHelper callLogViewsHelper) {
- ViewStub stub = (ViewStub) callLogItem.findViewById(R.id.call_log_entry_actions_stub);
+ ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub);
if (stub != null) {
actionsView = (ViewGroup) stub.inflate();
}
@@ -266,20 +298,93 @@ public final class CallLogListItemViews {
callLogViewsHelper.setActionContentDescriptions(this);
}
- public static CallLogListItemViews fromView(Context context, View view) {
- return new CallLogListItemViews(
- context,
- (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
- view.findViewById(R.id.primary_action_view),
- PhoneCallDetailsViews.fromView(view),
- view.findViewById(R.id.call_log_row),
- (TextView) view.findViewById(R.id.call_log_day_group_label));
+ /**
+ * Expands or collapses the view containing the CALLBACK/REDIAL, VOICEMAIL and DETAILS action
+ * buttons.
+ *
+ * TODO: Reduce number of classes which need to be passed in to inflate the action view stub.
+ * 1) Instantiate them in this class, and store local references.
+ * 2) Set them on the CallLogListItemHelper and use it for inflation.
+ * 3) Implement a parent view for a call log list item, and store references in that class.
+ */
+ public void expandOrCollapseActions(
+ boolean isExpanded,
+ final CallLogAdapter.OnReportButtonClickListener onReportButtonClickListener,
+ View.OnClickListener actionListener,
+ PhoneNumberUtilsWrapper phoneNumberUtilsWrapper,
+ CallLogListItemHelper callLogViewsHelper) {
+ expandVoicemailTranscriptionView(isExpanded);
+
+ if (isExpanded) {
+ // Inflate the view stub if necessary, and wire up the event handlers.
+ inflateActionViewStub(onReportButtonClickListener, actionListener,
+ phoneNumberUtilsWrapper, callLogViewsHelper);
+
+ actionsView.setVisibility(View.VISIBLE);
+ actionsView.setAlpha(1.0f);
+ callLogEntryView.setBackgroundColor(mExpandedBackgroundColor);
+ callLogEntryView.setTranslationZ(mExpandedTranslationZ);
+ rootView.setTranslationZ(mExpandedTranslationZ); // WAR
+ } else {
+ // When recycling a view, it is possible the actionsView ViewStub was previously
+ // inflated so we should hide it in this case.
+ if (actionsView != null) {
+ actionsView.setVisibility(View.GONE);
+ }
+
+ callLogEntryView.setBackgroundColor(mCallLogBackgroundColor);
+ callLogEntryView.setTranslationZ(0);
+ rootView.setTranslationZ(0); // WAR
+ }
+ }
+
+ public void expandVoicemailTranscriptionView(boolean isExpanded) {
+ if (callType != Calls.VOICEMAIL_TYPE) {
+ return;
+ }
+
+ final TextView view = phoneCallDetailsViews.voicemailTranscriptionView;
+ if (TextUtils.isEmpty(view.getText())) {
+ return;
+ }
+ view.setMaxLines(isExpanded ? VOICEMAIL_TRANSCRIPTION_MAX_LINES : 1);
+ view.setSingleLine(!isExpanded);
+ }
+
+ public void setPhoto(long photoId, Uri photoUri, Uri contactUri, String displayName,
+ boolean isVoicemail, boolean isBusiness) {
+ quickContactView.assignContactUri(contactUri);
+ quickContactView.setOverlay(null);
+
+ int contactType = ContactPhotoManager.TYPE_DEFAULT;
+ if (isVoicemail) {
+ contactType = ContactPhotoManager.TYPE_VOICEMAIL;
+ } else if (isBusiness) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
+ }
+
+ String lookupKey = null;
+ if (contactUri != null) {
+ lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri);
+ }
+
+ DefaultImageRequest request = new DefaultImageRequest(
+ displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (photoId == 0 && photoUri != null) {
+ ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, photoUri,
+ mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
+ } else {
+ ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, photoId,
+ false /* darkTheme */, true /* isCircular */, request);
+ }
}
@NeededForTesting
public static CallLogListItemViews createForTest(Context context) {
CallLogListItemViews views = new CallLogListItemViews(
context,
+ new View(context),
new QuickContactBadge(context),
new View(context),
PhoneCallDetailsViews.createForTest(context),
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index da03b072f..8e8aa3ce1 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -14,9 +14,12 @@
package com.android.dialer.calllog;
+import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
import android.net.Uri;
+import android.provider.CallLog.Calls;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
@@ -24,6 +27,7 @@ import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import android.util.Log;
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.PhoneNumberHelper;
@@ -41,6 +45,8 @@ import java.util.List;
* Utility class to look up the contact information for a given number.
*/
public class ContactInfoHelper {
+ private static final String TAG = ContactInfoHelper.class.getSimpleName();
+
private final Context mContext;
private final String mCurrentCountryIso;
@@ -278,6 +284,107 @@ public class ContactInfoHelper {
}
/**
+ * Stores differences between the updated contact info and the current call log contact info.
+ *
+ * @param number The number of the contact.
+ * @param countryIso The country associated with this number.
+ * @param updatedInfo The updated contact info.
+ * @param callLogInfo The call log entry's current contact info.
+ */
+ public void updateCallLogContactInfo(String number, String countryIso, ContactInfo updatedInfo,
+ ContactInfo callLogInfo) {
+ final ContentValues values = new ContentValues();
+ boolean needsUpdate = false;
+
+ if (callLogInfo != null) {
+ if (!TextUtils.equals(updatedInfo.name, callLogInfo.name)) {
+ values.put(Calls.CACHED_NAME, updatedInfo.name);
+ needsUpdate = true;
+ }
+
+ if (updatedInfo.type != callLogInfo.type) {
+ values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.label, callLogInfo.label)) {
+ values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
+ needsUpdate = true;
+ }
+
+ if (!UriUtils.areEqual(updatedInfo.lookupUri, callLogInfo.lookupUri)) {
+ values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
+ needsUpdate = true;
+ }
+
+ // Only replace the normalized number if the new updated normalized number isn't empty.
+ if (!TextUtils.isEmpty(updatedInfo.normalizedNumber) &&
+ !TextUtils.equals(updatedInfo.normalizedNumber, callLogInfo.normalizedNumber)) {
+ values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.number, callLogInfo.number)) {
+ values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
+ needsUpdate = true;
+ }
+
+ if (updatedInfo.photoId != callLogInfo.photoId) {
+ values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
+ needsUpdate = true;
+ }
+
+ final Uri updatedPhotoUriContactsOnly =
+ UriUtils.nullForNonContactsUri(updatedInfo.photoUri);
+ if (!UriUtils.areEqual(updatedPhotoUriContactsOnly, callLogInfo.photoUri)) {
+ values.put(Calls.CACHED_PHOTO_URI,
+ UriUtils.uriToString(updatedPhotoUriContactsOnly));
+ needsUpdate = true;
+ }
+
+ if (!TextUtils.equals(updatedInfo.formattedNumber, callLogInfo.formattedNumber)) {
+ values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
+ needsUpdate = true;
+ }
+ } else {
+ // No previous values, store all of them.
+ values.put(Calls.CACHED_NAME, updatedInfo.name);
+ values.put(Calls.CACHED_NUMBER_TYPE, updatedInfo.type);
+ values.put(Calls.CACHED_NUMBER_LABEL, updatedInfo.label);
+ values.put(Calls.CACHED_LOOKUP_URI, UriUtils.uriToString(updatedInfo.lookupUri));
+ values.put(Calls.CACHED_MATCHED_NUMBER, updatedInfo.number);
+ values.put(Calls.CACHED_NORMALIZED_NUMBER, updatedInfo.normalizedNumber);
+ values.put(Calls.CACHED_PHOTO_ID, updatedInfo.photoId);
+ values.put(Calls.CACHED_PHOTO_URI, UriUtils.uriToString(
+ UriUtils.nullForNonContactsUri(updatedInfo.photoUri)));
+ values.put(Calls.CACHED_FORMATTED_NUMBER, updatedInfo.formattedNumber);
+ needsUpdate = true;
+ }
+
+ if (!needsUpdate) {
+ return;
+ }
+
+ try {
+ if (countryIso == null) {
+ mContext.getContentResolver().update(
+ Calls.CONTENT_URI_WITH_VOICEMAIL,
+ values,
+ Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
+ new String[]{ number });
+ } else {
+ mContext.getContentResolver().update(
+ Calls.CONTENT_URI_WITH_VOICEMAIL,
+ values,
+ Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
+ new String[]{ number, countryIso });
+ }
+ } catch (SQLiteFullException e) {
+ Log.e(TAG, "Unable to update contact info in call log db", e);
+ }
+ }
+
+ /**
* Parses the given URI to determine the original lookup key of the contact.
*/
public static String getLookupKeyFromUri(Uri lookupUri) {
@@ -296,6 +403,29 @@ public class ContactInfoHelper {
}
/**
+ * Returns the contact information stored in an entry of the call log.
+ *
+ * @param c A cursor pointing to an entry in the call log.
+ */
+ public static ContactInfo getContactInfo(Cursor c) {
+ ContactInfo info = new ContactInfo();
+
+ info.lookupUri = UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_LOOKUP_URI));
+ info.name = c.getString(CallLogQuery.CACHED_NAME);
+ info.type = c.getInt(CallLogQuery.CACHED_NUMBER_TYPE);
+ info.label = c.getString(CallLogQuery.CACHED_NUMBER_LABEL);
+ String matchedNumber = c.getString(CallLogQuery.CACHED_MATCHED_NUMBER);
+ info.number = matchedNumber == null ? c.getString(CallLogQuery.NUMBER) : matchedNumber;
+ info.normalizedNumber = c.getString(CallLogQuery.CACHED_NORMALIZED_NUMBER);
+ info.photoId = c.getLong(CallLogQuery.CACHED_PHOTO_ID);
+ info.photoUri = UriUtils.nullForNonContactsUri(
+ UriUtils.parseUriOrNull(c.getString(CallLogQuery.CACHED_PHOTO_URI)));
+ info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
+
+ return info;
+ }
+
+ /**
* Given a contact's sourceType, return true if the contact is a business
*
* @param sourceType sourceType of the contact. This is usually populated by
diff --git a/src/com/android/dialer/contactinfo/ContactInfoRequest.java b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
new file mode 100644
index 000000000..ec5c1198e
--- /dev/null
+++ b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.dialer.contactinfo;
+
+import android.text.TextUtils;
+
+import com.android.dialer.calllog.ContactInfo;
+import com.google.common.base.Objects;
+
+/**
+ * A request for contact details for the given number, used by the ContactInfoCache.
+ */
+public final class ContactInfoRequest {
+ /** The number to look-up. */
+ public final String number;
+ /** The country in which a call to or from this number was placed or received. */
+ public final String countryIso;
+ /** The cached contact information stored in the call log. */
+ public final ContactInfo callLogInfo;
+
+ public ContactInfoRequest(String number, String countryIso, ContactInfo callLogInfo) {
+ this.number = number;
+ this.countryIso = countryIso;
+ this.callLogInfo = callLogInfo;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof ContactInfoRequest)) return false;
+
+ ContactInfoRequest other = (ContactInfoRequest) obj;
+
+ if (!TextUtils.equals(number, other.number)) return false;
+ if (!TextUtils.equals(countryIso, other.countryIso)) return false;
+ if (!Objects.equal(callLogInfo, other.callLogInfo)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((callLogInfo == null) ? 0 : callLogInfo.hashCode());
+ result = prime * result + ((countryIso == null) ? 0 : countryIso.hashCode());
+ result = prime * result + ((number == null) ? 0 : number.hashCode());
+ return result;
+ }
+}
diff --git a/src/com/android/dialer/contactinfo/NumberWithCountryIso.java b/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
new file mode 100644
index 000000000..1383fb7e9
--- /dev/null
+++ b/src/com/android/dialer/contactinfo/NumberWithCountryIso.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.dialer.contactinfo;
+
+import android.text.TextUtils;
+
+/**
+ * Stores a phone number of a call with the country code where it originally occurred. This object
+ * is used as a key in the {@code ContactInfoCache}.
+ *
+ * The country does not necessarily specify the country of the phone number itself, but rather
+ * it is the country in which the user was in when the call was placed or received.
+ */
+public final class NumberWithCountryIso {
+ public final String number;
+ public final String countryIso;
+
+ public NumberWithCountryIso(String number, String countryIso) {
+ this.number = number;
+ this.countryIso = countryIso;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (!(o instanceof NumberWithCountryIso)) return false;
+ NumberWithCountryIso other = (NumberWithCountryIso) o;
+ return TextUtils.equals(number, other.number)
+ && TextUtils.equals(countryIso, other.countryIso);
+ }
+
+ @Override
+ public int hashCode() {
+ int numberHashCode = number == null ? 0 : number.hashCode();
+ int countryHashCode = countryIso == null ? 0 : countryIso.hashCode();
+
+ return numberHashCode ^ countryHashCode;
+ }
+}