Take into account special dialable characters when grouping calls in the call log UI.

Currently we use PhoneNumberUtils#compare(String, String) to determine whether two numbers in the call log should be grouped. The method ignores special dialable characters such as "#", "*", "+", etc, which can cause phone numbers and service dialing numbers to be grouped.

For example, suppose the user has a contact named "John Smith" with number 123456789 and a contact named "Service" with number #123456789, and called each contact once. The old grouping logic will put the two numbers in the same group.

The new grouping logic in this CL can separate the two numbers.

Bug: 30225112
Test: CallLogGroupBuilderTest, PhoneNumberHelperTest
PiperOrigin-RevId: 172683494
Change-Id: Ie9e7b7418d6d7c74830cbae09c04ff1feb1b827d
diff --git a/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java
index 513c8aa..f689571 100644
--- a/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java
+++ b/java/com/android/dialer/app/calllog/CallLogGroupBuilder.java
@@ -21,7 +21,6 @@
 import android.os.Build.VERSION_CODES;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.text.format.Time;
 import com.android.contacts.common.util.DateUtils;
@@ -194,7 +193,7 @@
     if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
       return compareSipAddresses(number1, number2);
     } else {
-      return PhoneNumberUtils.compare(number1, number2);
+      return PhoneNumberHelper.compare(number1, number2);
     }
   }
 
diff --git a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
index 84cbb69..a53676b 100644
--- a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
+++ b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
@@ -34,6 +34,7 @@
 import com.android.dialer.telecom.TelecomUtil;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 
 public class PhoneNumberHelper {
@@ -50,7 +51,46 @@
   }
 
   /**
-   * Find the cursor pointing to a number that matches the number in a contact lookup URI.
+   * Compare two phone numbers, return true if they're identical enough for caller ID purposes. This
+   * is an enhanced version of {@link PhoneNumberUtils#compare(String, String)}.
+   *
+   * <p>The two phone numbers are considered "identical enough" if
+   *
+   * <ul>
+   *   <li>their corresponding raw numbers are both global phone numbers (i.e., they can be accepted
+   *       by {@link PhoneNumberUtils#isGlobalPhoneNumber(String)}) and {@link
+   *       PhoneNumberUtils#compare(String, String)} deems them as "identical enough"; OR
+   *   <li>at least one of the raw numbers is not a global phone number and the two raw numbers are
+   *       exactly the same.
+   * </ul>
+   *
+   * The raw number of a phone number is obtained by first translating any alphabetic letters
+   * ([A-Za-z]) into the equivalent numeric digits and then removing all separators. See {@link
+   * PhoneNumberUtils#convertKeypadLettersToDigits(String)} and {@link
+   * PhoneNumberUtils#stripSeparators(String)}.
+   */
+  public static boolean compare(@Nullable String number1, @Nullable String number2) {
+    if (number1 == null || number2 == null) {
+      return Objects.equals(number1, number2);
+    }
+
+    String rawNumber1 =
+        PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number1));
+    String rawNumber2 =
+        PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number2));
+
+    return PhoneNumberUtils.isGlobalPhoneNumber(rawNumber1)
+            && PhoneNumberUtils.isGlobalPhoneNumber(rawNumber2)
+        ? PhoneNumberUtils.compare(rawNumber1, rawNumber2)
+        : rawNumber1.equals(rawNumber2);
+  }
+
+  /**
+   * Find the cursor pointing to the row in which a number is identical enough to the number in a
+   * contact lookup URI.
+   *
+   * <p>See the description of {@link PhoneNumberHelper#compare(String, String)} for the definition
+   * of "identical enough".
    *
    * <p>When determining whether two phone numbers are identical enough for caller ID purposes, the
    * Contacts Provider uses {@link PhoneNumberUtils#compare(String, String)}, which ignores special
@@ -58,25 +98,17 @@
    * by the Contacts Provider to have multiple rows even when the URI asks for a specific number.
    *
    * <p>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.
-   *
-   * <p>If the cursor points to a global phone number (i.e., a number that can be accepted by {@link
-   * PhoneNumberUtils#isGlobalPhoneNumber(String)}) and the lookup number in the URI is a PARTIAL
-   * match, return the cursor.
-   *
-   * <p>If the cursor points to a number that is not a global phone number, return the cursor iff
-   * the lookup number in the URI is an EXACT match.
-   *
-   * <p>Return null in all other circumstances.
+   * respectively. When the URI asks for number "123", both numbers will be returned. Therefore,
+   * {@link PhoneNumberHelper#compare(String, String)}, which is an enhanced version of {@link
+   * PhoneNumberUtils#compare(String, String)}, is employed to find a match.
    *
    * @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 The cursor considered as a match by the description above or null if no such cursor can
-   *     be found.
+   * @return The cursor pointing to the row in which the number is considered a match by the
+   *     description above or null if no such cursor can be found.
    */
   public static Cursor getCursorMatchForContactLookupUri(
       Cursor cursor, int columnIndexForNumber, Uri contactLookupUri) {
@@ -96,22 +128,10 @@
       return null;
     }
 
-    boolean isMatchFound;
     do {
-      // All undialable characters should be converted/removed before comparing the lookup number
-      // and the existing contact number.
-      String rawExistingContactNumber =
-          PhoneNumberUtils.stripSeparators(
-              PhoneNumberUtils.convertKeypadLettersToDigits(
-                  cursor.getString(columnIndexForNumber)));
-      String rawQueryNumber =
-          PhoneNumberUtils.stripSeparators(
-              PhoneNumberUtils.convertKeypadLettersToDigits(lookupNumber));
+      String existingContactNumber = cursor.getString(columnIndexForNumber);
 
-      isMatchFound =
-          PhoneNumberUtils.isGlobalPhoneNumber(rawExistingContactNumber)
-              ? rawExistingContactNumber.contains(rawQueryNumber)
-              : rawExistingContactNumber.equals(rawQueryNumber);
+      boolean isMatchFound = compare(existingContactNumber, lookupNumber);
 
       if (isMatchFound) {
         return cursor;