Apply NANP logic to numbers with a +1 country code

Bug 8769688

Change-Id: I4aadd0f9e5495fb1b604910306dfd918d1540136
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
index f8877c6..f7ae1c2 100644
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
@@ -18,6 +18,8 @@
 
 import android.text.TextUtils;
 
+import com.android.dialer.dialpad.SmartDialTrie.CountryCodeWithOffset;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 
@@ -426,8 +428,19 @@
      * @return Phone number consisting of digits from 0-9
      */
     public static String normalizeNumber(String number) {
+        return normalizeNumber(number, 0);
+    }
+
+    /**
+     * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
+     *
+     * @param number Phone number we want to normalize
+     * @param offset Offset to start from
+     * @return Phone number consisting of digits from 0-9
+     */
+    public static String normalizeNumber(String number, int offset) {
         final StringBuilder s = new StringBuilder();
-        for (int i = 0; i < number.length(); i++) {
+        for (int i = offset; i < number.length(); i++) {
             char ch = number.charAt(i);
             if (ch >= '0' && ch <= '9') {
                 s.append(ch);
@@ -452,10 +465,12 @@
         SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
         if (matchPos == null) {
             // Try matching the number without the '+' prefix, if any
-            int offset = SmartDialTrie.getOffsetWithoutCountryCode(phoneNumber);
-            if (offset > 0) {
-                matchPos = matchesNumberWithOffset(phoneNumber, query, offset);
-            } else if (matchNanp) {
+            final CountryCodeWithOffset code = SmartDialTrie.getOffsetWithoutCountryCode(
+                    phoneNumber);
+            if (code != null) {
+                matchPos = matchesNumberWithOffset(phoneNumber, query, code.offset);
+            }
+            if (matchPos == null && matchNanp) {
                 // Try matching NANP numbers
                 final int[] offsets = SmartDialTrie.getOffsetForNANPNumbers(phoneNumber);
                 for (int i = 0; i < offsets.length; i++) {
diff --git a/src/com/android/dialer/dialpad/SmartDialTrie.java b/src/com/android/dialer/dialpad/SmartDialTrie.java
index 04ff64c..ab935b7 100644
--- a/src/com/android/dialer/dialpad/SmartDialTrie.java
+++ b/src/com/android/dialer/dialpad/SmartDialTrie.java
@@ -45,13 +45,33 @@
         int nthLastTokenPos;
     }
 
-    private static final int LAST_TOKENS_FOR_INITIALS = 2;
-    private static final int FIRST_TOKENS_FOR_INITIALS = 2;
+    /**
+     * A country code and integer offset pair that represents the parsed country code in a
+     * phone number. The country code is a string containing the numeric country-code prefix in
+     * a phone number (e.g. 1 or 852). The offset is the integer position of where the country code
+     * ends in a phone number.
+     */
+    public static class CountryCodeWithOffset {
+        public static final CountryCodeWithOffset NO_COUNTRY_CODE = new CountryCodeWithOffset(0,
+                "");
+
+        final String countryCode;
+        final int offset;
+
+        public CountryCodeWithOffset(int offset, String countryCode) {
+            this.countryCode = countryCode;
+            this.offset = offset;
+        }
+    }
+
     final Node mRoot = new Node();
     private int mSize = 0;
     private final char[] mCharacterMap;
     private final boolean mFormatNanp;
 
+    private static final int LAST_TOKENS_FOR_INITIALS = 2;
+    private static final int FIRST_TOKENS_FOR_INITIALS = 2;
+
     // Static set of all possible country codes in the world
     public static Set<String> sCountryCodes = null;
 
@@ -134,12 +154,14 @@
         // Strip the calling code from the phone number here
         if (!TextUtils.isEmpty(contact.phoneNumber)) {
             // Handle country codes for numbers with a + prefix
-            final int offset = getOffsetWithoutCountryCode(contact.phoneNumber);
-            if (offset > 0) {
-                putNumber(contact, contact.phoneNumber, offset);
-            } else if (mFormatNanp) {
+            final CountryCodeWithOffset code = getOffsetWithoutCountryCode(contact.phoneNumber);
+            if (code.offset != 0) {
+                putNumber(contact, contact.phoneNumber, code.offset);
+            }
+            if ((code.countryCode.equals("1") || code.offset == 0) && mFormatNanp) {
                 // Special case handling for NANP numbers (1-xxx-xxx-xxxx)
-                final String stripped = SmartDialNameMatcher.normalizeNumber(contact.phoneNumber);
+                final String stripped = SmartDialNameMatcher.normalizeNumber(
+                        contact.phoneNumber, code.offset);
                 if (!TextUtils.isEmpty(stripped)) {
                     int trunkPrefixOffset = 0;
                     if (stripped.charAt(0) == '1') {
@@ -159,24 +181,25 @@
                     }
                 }
             }
-            putNumber(contact, contact.phoneNumber);
+            putNumber(contact, contact.phoneNumber, 0);
         }
         mSize++;
     }
 
-    public static int getOffsetWithoutCountryCode(String number) {
+    public static CountryCodeWithOffset getOffsetWithoutCountryCode(String number) {
         if (!TextUtils.isEmpty(number)) {
             if (number.charAt(0) == '+') {
                 // check for international code here
                 for (int i = 1; i <= 1 + 3; i++) {
                     if (number.length() <= i) break;
-                    if (isValidCountryCode(number.substring(1, i))) {
-                        return i;
+                    final String countryCode = number.substring(1, i);
+                    if (isValidCountryCode(countryCode)) {
+                        return new CountryCodeWithOffset(i, countryCode);
                     }
                 }
             }
         }
-        return -1;
+        return CountryCodeWithOffset.NO_COUNTRY_CODE;
     }
 
     /**
@@ -278,16 +301,6 @@
      *
      * @param contact - Contact to add to the trie
      * @param phoneNumber - Phone number of the contact
-    */
-    private void putNumber(ContactNumber contact, String phoneNumber) {
-        putNumber(contact, phoneNumber, 0);
-    }
-
-    /**
-     * Puts a phone number and its associated contact into the prefix trie.
-     *
-     * @param contact - Contact to add to the trie
-     * @param phoneNumber - Phone number of the contact
      * @param offSet - The nth character of the phone number to start from
      */
     private void putNumber(ContactNumber contact, String phoneNumber, int offSet) {
diff --git a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
index 83b8560..eb6f050 100644
--- a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
+++ b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
@@ -201,6 +201,17 @@
         checkMatchesNumber("1-510-333-7596", "5103337596", true, true, 2, 14);
         checkMatchesNumber("1-510-333-7596", "3337596", true, true, 6, 14);
 
+        // An 11 digit number prefixed with +1 should be matched by the 10 digit number, as well as
+        // the 7 digit number (without area code)
+        checkMatchesNumber("+1-510-333-7596", "5103337596", true, true, 3, 15);
+        checkMatchesNumber("+1-510-333-7596", "3337596", true, true, 7, 15);
+        checkMatchesNumber("+1-510-333-7596", "103337596", false, true, 0, 0);
+        checkMatchesNumber("+1-510-333-7596", "337596", false, true, 0, 0);
+        checkMatchesNumber("+1510 3337596", "5103337596", true, true, 2, 13);
+        checkMatchesNumber("+1510 3337596", "3337596", true, true, 6, 13);
+        checkMatchesNumber("+1510 3337596", "103337596", false, true, 0, 0);
+        checkMatchesNumber("+1510 3337596", "37596", false, true, 0, 0);
+
         // Invalid NANP numbers should not be matched
         checkMatchesNumber("1-510-333-759", "510333759", false, true, 0, 0);
         checkMatchesNumber("510-333-759", "333759", false, true, 0, 0);
diff --git a/tests/src/com/android/dialer/dialpad/SmartDialTrieTest.java b/tests/src/com/android/dialer/dialpad/SmartDialTrieTest.java
index 876e00d..f0c4cbb 100644
--- a/tests/src/com/android/dialer/dialpad/SmartDialTrieTest.java
+++ b/tests/src/com/android/dialer/dialpad/SmartDialTrieTest.java
@@ -294,7 +294,6 @@
 
     // Tests special case handling for NANP numbers
     public void testPutNumbersNANP() {
-
         final SmartDialTrie trie = new SmartDialTrie(true /* formatNanp */);
         // Unformatted number with 1 prefix
         final ContactNumber contactno1 = new ContactNumber(0, "James", "16503337596", "0", 1);
@@ -340,6 +339,29 @@
         // But the NANP special case handling should not work
         assertFalse(checkContains(trie, contactno6, "123123"));
 
+        // Number with +1 prefix and is a NANP number
+        final ContactNumber contactno7 = new ContactNumber(0, "Mike", "+1-510-284-9170", "0", 1);
+        trie.put(contactno7);
+        assertTrue(checkContains(trie, contactno7, "15102849170"));
+        assertTrue(checkContains(trie, contactno7, "5102849170"));
+        assertTrue(checkContains(trie, contactno7, "2849170"));
+        assertFalse(checkContains(trie, contactno7, "849170"));
+        assertFalse(checkContains(trie, contactno7, "10849170"));
+
+        // Number with +1 prefix but is an invalid NANP number
+        final ContactNumber contactno8 = new ContactNumber(0, "Invalid", "+1-510-284-917", "0", 1);
+        trie.put(contactno8);
+        assertTrue(checkContains(trie, contactno8, "1510284917"));
+        assertTrue(checkContains(trie, contactno8, "510284917"));
+        assertFalse(checkContains(trie, contactno8, "2849170"));
+
+        // Number with invalid country code prefix
+        final ContactNumber contactno9 = new ContactNumber(0, "Inv", "+857-510-284-9170", "0", 1);
+        trie.put(contactno9);
+        assertTrue(checkContains(trie, contactno9, "8575102849170"));
+        assertFalse(checkContains(trie, contactno9, "5102849170"));
+        assertFalse(checkContains(trie, contactno9, "2849170"));
+
         // If user's region is determined to be not in North America, then the NANP number
         // workarounds should not be applied
         final SmartDialTrie trieNonNANP = new SmartDialTrie();