Support auto-complete in PhoneNumberFormattingTextWatcher. Also, simplify logic.
Previously, the assumption was that only a deletion or an insertion can happen
at a time; but with auto-complete, a deletion and insertion happen at the same time.

Needed for bug 5992672 in the Messaging app and is nice to have in the platform.

Change-Id: I2d80cecc486e7a1ceeaeb34866bcd834074f5ead
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index 6f85c7d..983c349 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -39,30 +39,6 @@
  * The formatting will be restarted once the text is cleared.
  */
 public class PhoneNumberFormattingTextWatcher implements TextWatcher {
-    /**
-     * One or more characters were removed from the end.
-     */
-    private final static int STATE_REMOVE_LAST = 0;
-
-    /**
-     * One or more characters were appended.
-     */
-    private final static int STATE_APPEND = 1;
-
-    /**
-     * One or more digits were changed in the beginning or the middle of text.
-     */
-    private final static int STATE_MODIFY_DIGITS = 2;
-
-    /**
-     * The changes other than the above.
-     */
-    private final static int STATE_OTHER = 3;
-
-    /**
-     * The state of this change could be one value of the above
-     */
-    private int mState;
 
     /**
      * Indicates the change was caused by ourselves.
@@ -97,46 +73,30 @@
         mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
     }
 
+    @Override
     public void beforeTextChanged(CharSequence s, int start, int count,
             int after) {
         if (mSelfChange || mStopFormatting) {
             return;
         }
-        if (count == 0 && s.length() == start) {
-            // Append one or more new chars
-            mState = STATE_APPEND;
-        } else if (after == 0 && start + count == s.length() && count > 0) {
-            // Remove one or more chars from the end of string.
-            mState = STATE_REMOVE_LAST;
-        } else if (count > 0 && !hasSeparator(s, start, count)) {
-            // Remove the dialable chars in the begin or middle of text.
-            mState = STATE_MODIFY_DIGITS;
-        } else {
-            mState = STATE_OTHER;
+        // If the user manually deleted any non-dialable characters, stop formatting
+        if (count > 0 && hasSeparator(s, start, count)) {
+            stopFormatting();
         }
     }
 
+    @Override
     public void onTextChanged(CharSequence s, int start, int before, int count) {
         if (mSelfChange || mStopFormatting) {
             return;
         }
-        if (mState == STATE_OTHER) {
-            if (count > 0 && !hasSeparator(s, start, count)) {
-                // User inserted the dialable characters in the middle of text.
-                mState = STATE_MODIFY_DIGITS;
-            }
-        }
-        // Check whether we should stop formatting.
-        if (mState == STATE_APPEND && count > 0 && hasSeparator(s, start, count)) {
-            // User appended the non-dialable character, stop formatting.
-            stopFormatting();
-        } else if (mState == STATE_OTHER) {
-            // User must insert or remove the non-dialable characters in the begin or middle of
-            // number, stop formatting.
+        // If the user inserted any non-dialable characters, stop formatting
+        if (count > 0 && hasSeparator(s, start, count)) {
             stopFormatting();
         }
     }
 
+    @Override
     public synchronized void afterTextChanged(Editable s) {
         if (mStopFormatting) {
             // Restart the formatting when all texts were clear.
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
index 6f0175e..a6a0fce 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
@@ -182,14 +182,17 @@
 
     public void testTextChangedByOtherTextWatcher() {
         final TextWatcher cleanupTextWatcher = new TextWatcher() {
+            @Override
             public void afterTextChanged(Editable s) {
                 s.clear();
             }
 
+            @Override
             public void beforeTextChanged(CharSequence s, int start, int count,
                     int after) {
             }
 
+            @Override
             public void onTextChanged(CharSequence s, int start, int before,
                     int count) {
             }
@@ -208,6 +211,81 @@
         assertEquals(expected1, number.toString());
     }
 
+    /**
+     * Test the case where some other component is auto-completing what the user is typing
+     */
+    public void testAutoCompleteWithFormattedNumber() {
+        String init = "650-1";
+        String expected = "+1-650-123-4567"; // Different formatting than ours
+        testReplacement(init, expected, expected);
+    }
+
+    /**
+     * Test the case where some other component is auto-completing what the user is typing
+     */
+    public void testAutoCompleteWithFormattedNameAndNumber() {
+        String init = "650-1";
+        String expected = "Test User <650-123-4567>";
+        testReplacement(init, expected, expected);
+    }
+
+    /**
+     * Test the case where some other component is auto-completing what the user is typing
+     */
+    public void testAutoCompleteWithNumericNameAndNumber() {
+        String init = "650";
+        String expected = "2nd Test User <650-123-4567>";
+        testReplacement(init, expected, expected);
+    }
+
+    /**
+     * Test the case where some other component is auto-completing what the user is typing
+     */
+    public void testAutoCompleteWithUnformattedNumber() {
+        String init = "650-1";
+        String expected = "6501234567";
+        testReplacement(init, expected, expected);
+    }
+
+    /**
+     * Test the case where some other component is auto-completing what the user is typing, where
+     * the deleted text doesn't have any formatting and neither does the replacement text: in this
+     * case the replacement text should be formatted by the PhoneNumberFormattingTextWatcher.
+     */
+    public void testAutoCompleteUnformattedWithUnformattedNumber() {
+        String init = "650";
+        String replacement = "6501234567";
+        String expected = "(650) 123-4567";
+        testReplacement(init, replacement, expected);
+
+        String init2 = "650";
+        String replacement2 = "16501234567";
+        String expected2 = "1 650-123-4567";
+        testReplacement(init2, replacement2, expected2);
+    }
+
+    /**
+     * Helper method for testing replacing the entire string with another string
+     * @param init The initial string
+     * @param expected
+     */
+    private void testReplacement(String init, String replacement, String expected) {
+        TextWatcher textWatcher = getTextWatcher();
+
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+
+        // Replace entire text with the given values
+        textWatcher.beforeTextChanged(number, 0, init.length(), replacement.length());
+        number.replace(0, init.length(), replacement, 0, replacement.length());
+        Selection.setSelection(number, replacement.length()); // move the cursor to the end
+        textWatcher.onTextChanged(number, 0, init.length(), replacement.length());
+        textWatcher.afterTextChanged(number);
+
+        assertEquals(expected, number.toString());
+        // the cursor should be still at the end
+        assertEquals(expected.length(), Selection.getSelectionEnd(number));
+    }
+
     private TextWatcher getTextWatcher() {
         return new PhoneNumberFormattingTextWatcher("US");
     }