From 2d5167b475d64b4bad0f0cde2508bca55c4b7643 Mon Sep 17 00:00:00 2001 From: linyuh Date: Thu, 14 Dec 2017 12:34:31 -0800 Subject: Take into consideration special characters when we do contact match. Bug: 30225112 Test: CallLogGroupBuilderTest, PhoneNumberHelperTest, CallerInfoTest, and Manual (see demo) PiperOrigin-RevId: 179080046 Change-Id: I8e451a6c197a6c3df4260e58d0276a5dc5b9515a --- .../dialer/app/calllog/CallLogGroupBuilder.java | 17 ++++- .../dialer/phonenumbercache/ContactInfoHelper.java | 11 +++ .../dialer/phonenumberutil/PhoneNumberHelper.java | 88 ++++++++++++++++++++++ java/com/android/incallui/CallerInfo.java | 22 ++++-- 4 files changed, 130 insertions(+), 8 deletions(-) (limited to 'java/com') diff --git a/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java index 513c8aa59..a48de0f51 100644 --- a/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java +++ b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java @@ -189,13 +189,26 @@ public class CallLogGroupBuilder { mGroupCreator.addGroup(count - groupSize, groupSize); } + /** + * Returns true when the two input numbers can be considered identical enough for caller ID + * purposes and put in a call log group. + */ @VisibleForTesting boolean equalNumbers(@Nullable String number1, @Nullable String number2) { if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) { return compareSipAddresses(number1, number2); - } else { - return PhoneNumberUtils.compare(number1, number2); } + + // PhoneNumberUtils.compare(String, String) ignores special characters such as '#'. For example, + // it thinks "123" and "#123" are identical enough for caller ID purposes. + // When either input number contains special characters, we put the two in the same group iff + // their raw numbers are exactly the same. + if (PhoneNumberHelper.numberHasSpecialChars(number1) + || PhoneNumberHelper.numberHasSpecialChars(number2)) { + return PhoneNumberHelper.sameRawNumbers(number1, number2); + } + + return PhoneNumberUtils.compare(number1, number2); } private boolean isSameAccount(String name1, String name2, String id1, String id2) { diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java index 39e3866cf..1fd2bffd9 100644 --- a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java +++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java @@ -355,6 +355,17 @@ public class ContactInfoHelper { return ContactInfo.EMPTY; } + // The Contacts provider ignores special characters in phone numbers when searching for a + // contact. For example, number "123" is considered a match with a contact with number "#123". + // We need to check whether the result contains a number that truly matches the query and move + // the cursor to that position before building a ContactInfo. + boolean hasNumberMatch = + PhoneNumberHelper.updateCursorToMatchContactLookupUri( + phoneLookupCursor, PhoneQuery.MATCHED_NUMBER, uri); + if (!hasNumberMatch) { + return ContactInfo.EMPTY; + } + String lookupKey = phoneLookupCursor.getString(PhoneQuery.LOOKUP_KEY); ContactInfo contactInfo = createPhoneLookupContactInfo(phoneLookupCursor, lookupKey); fillAdditionalContactInfo(mContext, contactInfo); diff --git a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java index be1b0629a..12e146959 100644 --- a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java +++ b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java @@ -17,6 +17,8 @@ package com.android.dialer.phonenumberutil; import android.content.Context; +import android.database.Cursor; +import android.net.Uri; import android.os.Trace; import android.provider.CallLog; import android.support.annotation.NonNull; @@ -27,6 +29,7 @@ import android.telephony.TelephonyManager; import android.text.BidiFormatter; import android.text.TextDirectionHeuristics; import android.text.TextUtils; +import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.compat.CompatUtils; import com.android.dialer.compat.telephony.TelephonyManagerCompat; @@ -49,6 +52,91 @@ public class PhoneNumberHelper { && !isLegacyUnknownNumbers(number); } + /** + * Move the given cursor to a position where the number it points to matches the number in a + * contact lookup URI. + * + *

We assume the cursor is one returned by the Contacts Provider when the URI asks for a + * specific number. This method's behavior is undefined when the cursor doesn't meet the + * assumption. + * + *

When determining whether two phone numbers are identical enough for caller ID purposes, the + * Contacts Provider ignores special characters such as '#'. This makes it possible for the cursor + * returned by the Contacts Provider to have multiple rows even when the URI asks for a specific + * number. + * + *

For example, suppose the user has two contacts whose numbers are "#123" and "123", + * respectively. When the URI asks for number "123", both numbers will be returned. Therefore, the + * following strategy is employed to find a match. + * + *

In the following description, we use E to denote a number the cursor points to (an existing + * contact number), and L to denote the number in the contact lookup URI. + * + *

If neither E nor L contains special characters, return true to indicate a match is found. + * + *

If either E or L contains special characters, return true when the raw numbers of E and L + * are the same. Otherwise, move the cursor to its next position and start over. + * + *

Return false in all other circumstances to indicate that no match can be found. + * + *

When no match can be found, the cursor is after the last result when the method returns. + * + * @param cursor A cursor returned by the Contacts Provider. + * @param columnIndexForNumber The index of the column where phone numbers are stored. It is the + * caller's responsibility to pass the correct column index. + * @param contactLookupUri A URI used to retrieve a contact via the Contacts Provider. It is the + * caller's responsibility to ensure the URI is one that asks for a specific phone number. + * @return true if a match can be found. + */ + public static boolean updateCursorToMatchContactLookupUri( + @Nullable Cursor cursor, int columnIndexForNumber, @Nullable Uri contactLookupUri) { + if (cursor == null || contactLookupUri == null) { + return false; + } + + if (!cursor.moveToFirst()) { + return false; + } + + Assert.checkArgument( + 0 <= columnIndexForNumber && columnIndexForNumber < cursor.getColumnCount()); + + String lookupNumber = contactLookupUri.getLastPathSegment(); + if (TextUtils.isEmpty(lookupNumber)) { + return false; + } + + boolean lookupNumberHasSpecialChars = numberHasSpecialChars(lookupNumber); + + do { + String existingContactNumber = cursor.getString(columnIndexForNumber); + boolean existingContactNumberHasSpecialChars = numberHasSpecialChars(existingContactNumber); + + if ((!lookupNumberHasSpecialChars && !existingContactNumberHasSpecialChars) + || sameRawNumbers(existingContactNumber, lookupNumber)) { + return true; + } + + } while (cursor.moveToNext()); + + return false; + } + + /** Returns true if the input phone number contains special characters. */ + public static boolean numberHasSpecialChars(String number) { + return !TextUtils.isEmpty(number) && number.contains("#"); + } + + /** Returns true if the raw numbers of the two input phone numbers are the same. */ + public static boolean sameRawNumbers(String number1, String number2) { + String rawNumber1 = + PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number1)); + String rawNumber2 = + PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number2)); + + return rawNumber1.equals(rawNumber2); + } + /** * Returns true if the given number is the number of the configured voicemail. To be able to * mock-out this, it is not a static method. diff --git a/java/com/android/incallui/CallerInfo.java b/java/com/android/incallui/CallerInfo.java index 5c43b4f39..d3b12c158 100644 --- a/java/com/android/incallui/CallerInfo.java +++ b/java/com/android/incallui/CallerInfo.java @@ -223,16 +223,26 @@ public class CallerInfo { long contactId = 0L; int columnIndex; - // Look for the name - columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME); + // Look for the number + columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER); if (columnIndex != -1) { - info.name = normalize(cursor.getString(columnIndex)); + // The Contacts provider ignores special characters in phone numbers when searching for a + // contact. For example, number "123" is considered a match with a contact with number "#123". + // We need to check whether the result contains a number that truly matches the query and move + // the cursor to that position before filling in the fields in CallerInfo. + boolean hasNumberMatch = + PhoneNumberHelper.updateCursorToMatchContactLookupUri(cursor, columnIndex, contactRef); + if (hasNumberMatch) { + info.phoneNumber = cursor.getString(columnIndex); + } else { + return info; + } } - // Look for the number - columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER); + // Look for the name + columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME); if (columnIndex != -1) { - info.phoneNumber = cursor.getString(columnIndex); + info.name = normalize(cursor.getString(columnIndex)); } // Look for the normalized number -- cgit v1.2.3