Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 731f8615 authored by Flavio Lerda's avatar Flavio Lerda
Browse files

Share contact look up between call log and call details.

Currently, it is possible to get a contact be correctly identified in
the call log, but not in the details, because the logic to do the
look-up are completely separate.

For example, SIP contacts are correctly looked up by the call log, but
not by the details. Moreover, we recently fixed a few bugs in the logic
for looking up contacts in the call log, that have not been propagated
to the call details because the code is independent.

This commit moves the whole logic of looking up a contact given a number
(which may be a SIP number) from the CallLogAdapter into its own class.
Then, CallDetailActivity can use that to look up the contact in a way
that is guaranteed to yield the same result.

Bug: 5415134
Change-Id: I73acc39ba5dc65ecce6861f210224304fd91822d
parent e810bdb4
Loading
Loading
Loading
Loading
+29 −62
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.contacts;
import com.android.contacts.BackScrollManager.ScrollableHeader;
import com.android.contacts.calllog.CallDetailHistoryAdapter;
import com.android.contacts.calllog.CallTypeHelper;
import com.android.contacts.calllog.ContactInfo;
import com.android.contacts.calllog.ContactInfoHelper;
import com.android.contacts.calllog.PhoneNumberHelper;
import com.android.contacts.util.AsyncTaskExecutor;
import com.android.contacts.util.AsyncTaskExecutors;
@@ -44,7 +46,6 @@ import android.provider.CallLog.Calls;
import android.provider.Contacts.Intents.Insert;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.VoicemailContract.Voicemails;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
@@ -101,6 +102,7 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
    private ImageButton mMainActionPushLayerView;
    private ImageView mContactBackgroundView;
    private AsyncTaskExecutor mAsyncTaskExecutor;
    private ContactInfoHelper mContactInfoHelper;

    private String mNumber = null;
    private String mDefaultCountryIso;
@@ -191,25 +193,6 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
    static final int COUNTRY_ISO_COLUMN_INDEX = 4;
    static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;

    static final String[] PHONES_PROJECTION = new String[] {
        PhoneLookup._ID,
        PhoneLookup.DISPLAY_NAME,
        PhoneLookup.TYPE,
        PhoneLookup.LABEL,
        PhoneLookup.NUMBER,
        PhoneLookup.NORMALIZED_NUMBER,
        PhoneLookup.PHOTO_URI,
        PhoneLookup.LOOKUP_KEY,
    };
    static final int COLUMN_INDEX_ID = 0;
    static final int COLUMN_INDEX_NAME = 1;
    static final int COLUMN_INDEX_TYPE = 2;
    static final int COLUMN_INDEX_LABEL = 3;
    static final int COLUMN_INDEX_NUMBER = 4;
    static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
    static final int COLUMN_INDEX_PHOTO_URI = 6;
    static final int COLUMN_INDEX_LOOKUP_KEY = 7;

    private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
@@ -251,6 +234,7 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
        mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
        mContactPhotoManager = ContactPhotoManager.getInstance(this);
        mProximitySensorManager = new ProximitySensorManager(this, mProximitySensorListener);
        mContactInfoHelper = new ContactInfoHelper(this, ContactsUtils.getCurrentCountryIso(this));
        configureActionBar();
        optionallyHandleVoicemail();
    }
@@ -570,53 +554,36 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
            }

            // Formatted phone number.
            final CharSequence numberText;
            final CharSequence formattedNumber;
            // Read contact specifics.
            CharSequence nameText = "";
            int numberType = 0;
            CharSequence numberLabel = "";
            Uri photoUri = null;
            Uri contactUri = null;
            final CharSequence nameText;
            final int numberType;
            final CharSequence numberLabel;
            final Uri photoUri;
            final Uri lookupUri;
            // 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()) {
                        nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
                        String photoUriString = phonesCursor.getString(COLUMN_INDEX_PHOTO_URI);
                        photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
                        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);
                        long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
                        if (personId > 0) {
                            contactUri = Contacts.getLookupUri(personId,
                                    phonesCursor.getString(COLUMN_INDEX_LOOKUP_KEY));
                        }
            ContactInfo info =
                    mPhoneNumberHelper.canPlaceCallsTo(number)
                            ? mContactInfoHelper.lookupNumber(number, countryIso)
                            : null;
            if (info == null) {
                formattedNumber = mPhoneNumberHelper.getDisplayNumber(number, null);
                nameText = "";
                numberType = 0;
                numberLabel = "";
                photoUri = null;
                lookupUri = null;
            } 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, countryIso, geocode,
                formattedNumber = info.formattedNumber;
                nameText = info.name;
                numberType = info.type;
                numberLabel = info.label;
                photoUri = info.photoUri;
                lookupUri = info.lookupUri;
            }
            return new PhoneCallDetails(number, formattedNumber, countryIso, geocode,
                    new int[]{ callType }, date, duration,
                    nameText, numberType, numberLabel, contactUri, photoUri);
                    nameText, numberType, numberLabel, lookupUri, photoUri);
        } finally {
            if (callCursor != null) {
                callCursor.close();
+8 −193
Original line number Diff line number Diff line
@@ -33,11 +33,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -65,7 +61,7 @@ public class CallLogAdapter extends GroupingListAdapter
    private static final int CONTACT_INFO_CACHE_SIZE = 100;

    private final Context mContext;
    private final String mCurrentCountryIso;
    private final ContactInfoHelper mContactInfoHelper;
    private final CallFetcher mCallFetcher;

    /**
@@ -198,12 +194,12 @@ public class CallLogAdapter extends GroupingListAdapter
    };

    public CallLogAdapter(Context context, CallFetcher callFetcher,
            String currentCountryIso, String voicemailNumber) {
            ContactInfoHelper contactInfoHelper, String voicemailNumber) {
        super(context);

        mContext = context;
        mCurrentCountryIso = currentCountryIso;
        mCallFetcher = callFetcher;
        mContactInfoHelper = contactInfoHelper;

        mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
        mRequests = new LinkedList<ContactInfoRequest>();
@@ -304,134 +300,6 @@ public class CallLogAdapter extends GroupingListAdapter
        }
    }

    /**
     * Determines the contact information for the given SIP address.
     * <p>
     * It returns the contact info if found.
     * <p>
     * If no contact corresponds to the given SIP address, returns {@link ContactInfo#EMPTY}.
     * <p>
     * If the lookup fails for some other reason, it returns null.
     */
    private ContactInfo queryContactInfoForSipAddress(String sipAddress) {
        final ContactInfo info;

        // TODO: This code is duplicated from the
        // CallerInfoAsyncQuery class.  To avoid that, could the
        // code here just use CallerInfoAsyncQuery, rather than
        // manually running ContentResolver.query() itself?

        // We look up SIP addresses directly in the Data table:
        Uri contactRef = Data.CONTENT_URI;

        // Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
        //
        // Also note we use "upper(data1)" in the WHERE clause, and
        // uppercase the incoming SIP address, in order to do a
        // case-insensitive match.
        //
        // TODO: May also need to normalize by adding "sip:" as a
        // prefix, if we start storing SIP addresses that way in the
        // database.
        String selection = "upper(" + Data.DATA1 + ")=?"
                + " AND "
                + Data.MIMETYPE + "='" + SipAddress.CONTENT_ITEM_TYPE + "'";
        String[] selectionArgs = new String[] { sipAddress.toUpperCase() };

        Cursor dataTableCursor =
                mContext.getContentResolver().query(
                        contactRef,
                        null,  // projection
                        selection,  // selection
                        selectionArgs,  // selectionArgs
                        null);  // sortOrder

        if (dataTableCursor != null) {
            if (dataTableCursor.moveToFirst()) {
                info = new ContactInfo();

                // TODO: we could slightly speed this up using an
                // explicit projection (and thus not have to do
                // those getColumnIndex() calls) but the benefit is
                // very minimal.

                // Note the Data.CONTACT_ID column here is
                // equivalent to the PERSON_ID_COLUMN_INDEX column
                // we use with "phonesCursor" below.
                long contactId = dataTableCursor.getLong(
                        dataTableCursor.getColumnIndex(Data.CONTACT_ID));
                String lookupKey = dataTableCursor.getString(
                        dataTableCursor.getColumnIndex(Data.LOOKUP_KEY));
                info.lookupUri = Contacts.getLookupUri(contactId, lookupKey);
                info.name = dataTableCursor.getString(
                        dataTableCursor.getColumnIndex(Data.DISPLAY_NAME));
                // "type" and "label" are currently unused for SIP addresses
                info.type = SipAddress.TYPE_OTHER;
                info.label = null;

                // And "number" is the SIP address.
                // Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
                info.number = dataTableCursor.getString(dataTableCursor.getColumnIndex(Data.DATA1));
                info.normalizedNumber = null;  // meaningless for SIP addresses
                info.photoId = dataTableCursor.getLong(
                        dataTableCursor.getColumnIndex(Data.PHOTO_ID));
                info.formattedNumber = null;  // meaningless for SIP addresses
            } else {
                info = ContactInfo.EMPTY;
            }
            dataTableCursor.close();
        } else {
            // Failed to fetch the data, ignore this request.
            info = null;
        }
        return info;
    }

    /**
     * Determines the contact information for the given phone number.
     * <p>
     * It returns the contact info if found.
     * <p>
     * If no contact corresponds to the given phone number, returns {@link ContactInfo#EMPTY}.
     * <p>
     * If the lookup fails for some other reason, it returns null.
     */
    private ContactInfo queryContactInfoForPhoneNumber(String number, String countryIso) {
        final ContactInfo info;

        // "number" is a regular phone number, so use the
        // PhoneLookup table:
        Cursor phonesCursor =
                mContext.getContentResolver().query(
                    Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
                            Uri.encode(number)),
                            PhoneQuery._PROJECTION, null, null, null);
        if (phonesCursor != null) {
            if (phonesCursor.moveToFirst()) {
                info = new ContactInfo();
                long contactId = phonesCursor.getLong(PhoneQuery.PERSON_ID);
                String lookupKey = phonesCursor.getString(PhoneQuery.LOOKUP_KEY);
                info.lookupUri = Contacts.getLookupUri(contactId, lookupKey);
                info.name = phonesCursor.getString(PhoneQuery.NAME);
                info.type = phonesCursor.getInt(PhoneQuery.PHONE_TYPE);
                info.label = phonesCursor.getString(PhoneQuery.LABEL);
                info.number = phonesCursor.getString(PhoneQuery.MATCHED_NUMBER);
                info.normalizedNumber = phonesCursor.getString(PhoneQuery.NORMALIZED_NUMBER);
                info.photoId = phonesCursor.getLong(PhoneQuery.PHOTO_ID);
                info.formattedNumber = formatPhoneNumber(info.number, info.formattedNumber,
                        countryIso);

            } else {
                info = ContactInfo.EMPTY;
            }
            phonesCursor.close();
        } else {
            // Failed to fetch the data, ignore this request.
            info = null;
        }
        return info;
    }

    /**
     * Queries the appropriate content provider for the contact associated with the number.
     * <p>
@@ -444,53 +312,25 @@ public class CallLogAdapter extends GroupingListAdapter
     * view to update its content.
     */
    private boolean queryContactInfo(String number, String countryIso, ContactInfo callLogInfo) {
        final ContactInfo info;

        // Determine the contact info.
        if (PhoneNumberUtils.isUriNumber(number)) {
            // This "number" is really a SIP address.
            ContactInfo sipInfo = queryContactInfoForSipAddress(number);
            if (sipInfo == null || sipInfo == ContactInfo.EMPTY) {
                // Check whether the username is actually a phone number of contact.
                String username = number.substring(0, number.indexOf('@'));
                if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
                    sipInfo = queryContactInfoForPhoneNumber(username, countryIso);
                }
            }
            info = sipInfo;
        } else {
            info = queryContactInfoForPhoneNumber(number, countryIso);
        }
        final ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);

        if (info == null) {
            // The lookup failed, just return without requesting to update the view.
            return false;
        }

        final ContactInfo updatedInfo;
        // If we did not find a matching contact, generate an empty contact info for the number.
        if (info == ContactInfo.EMPTY) {
            // Did not find a matching contact.
            updatedInfo = new ContactInfo();
            updatedInfo.number = number;
            updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
        } else {
            updatedInfo = info;
        }

        // Check the existing entry in the cache: only if it has changed we should update the
        // view.
        ContactInfo existingInfo = mContactInfoCache.getPossiblyExpired(number);
        boolean updated = !updatedInfo.equals(existingInfo);
        boolean updated = !info.equals(existingInfo);
        // 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(number, updatedInfo);
        mContactInfoCache.put(number, 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, updatedInfo, callLogInfo);
        updateCallLogContactInfoCache(number, info, callLogInfo);
        return updated;
    }

    /*
     * Handles requests for contact name and number type
     * @see java.lang.Runnable#run()
@@ -804,6 +644,7 @@ public class CallLogAdapter extends GroupingListAdapter
        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 = null;  // We do not cache the photo URI.
        info.formattedNumber = c.getString(CallLogQuery.CACHED_FORMATTED_NUMBER);
        return info;
    }
@@ -850,32 +691,6 @@ public class CallLogAdapter extends GroupingListAdapter
        super.addGroup(cursorPosition, size, expanded);
    }

    /**
     * Format the given phone number
     *
     * @param number the number to be formatted.
     * @param normalizedNumber the normalized number of the given number.
     * @param countryIso the ISO 3166-1 two letters country code, the country's
     *        convention will be used to format the number if the normalized
     *        phone is null.
     *
     * @return the formatted number, or the given number if it was formatted.
     */
    private String formatPhoneNumber(String number, String normalizedNumber,
            String countryIso) {
        if (TextUtils.isEmpty(number)) {
            return "";
        }
        // If "number" is really a SIP address, don't try to do any formatting at all.
        if (PhoneNumberUtils.isUriNumber(number)) {
            return number;
        }
        if (TextUtils.isEmpty(countryIso)) {
            countryIso = mCurrentCountryIso;
        }
        return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
    }

    /*
     * Get the number from the Contacts, if available, since sometimes
     * the number provided by caller id may not be formatted properly
+2 −1
Original line number Diff line number Diff line
@@ -152,7 +152,8 @@ public class CallLogFragment extends ListFragment implements ViewPagerVisibility
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        String currentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
        mAdapter = new CallLogAdapter(getActivity(), this, currentCountryIso, mVoiceMailNumber);
        mAdapter = new CallLogAdapter(getActivity(), this,
                new ContactInfoHelper(getActivity(), currentCountryIso), mVoiceMailNumber);
        setListAdapter(mAdapter);
        getListView().setItemsCanFocus(true);
    }
+3 −0
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ public final class ContactInfo {
    public String normalizedNumber;
    /** The photo for the contact, if available. */
    public long photoId;
    /** The high-res photo for the contact, if available. */
    public Uri photoUri;

    public static ContactInfo EMPTY = new ContactInfo();

@@ -63,6 +65,7 @@ public final class ContactInfo {
        if (!TextUtils.equals(formattedNumber, other.formattedNumber)) return false;
        if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) return false;
        if (photoId != other.photoId) return false;
        if (!UriUtils.areEqual(photoUri, other.photoUri)) return false;
        return true;
    }
}
+248 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading