diff options
Diffstat (limited to 'src')
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; + } +} |