Disable phone number formatting on the dialpad when making domestic call to Argentina mobile numbers.

Bug: 30224215
Test: DialerPhoneNumberFormattingTextWatcherTest, DialpadFragmentIntegrationTest
PiperOrigin-RevId: 177218942
Change-Id: Id6eaaad2f6d81e591d59a1d8444f45fb06f3f8d5
diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java
index 4126443..69186d8 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -89,8 +89,11 @@
 import com.android.dialer.util.CallUtil;
 import com.android.dialer.util.PermissionsUtil;
 import com.android.dialer.widget.FloatingActionButtonController;
+import com.google.common.base.Optional;
 import java.util.HashSet;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /** Fragment that displays a twelve-key phone dialpad. */
 public class DialpadFragment extends Fragment
@@ -129,6 +132,9 @@
   private static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH";
 
   private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
+
+  private static Optional<String> currentCountryIsoForTesting = Optional.absent();
+
   private final Object mToneGeneratorLock = new Object();
   /** Set of dialpad keys that are currently being pressed */
   private final HashSet<View> mPressedDialpadKeys = new HashSet<>(12);
@@ -156,7 +162,6 @@
 
   // determines if we want to playback local DTMF tones.
   private boolean mDTMFToneEnabled;
-  private String mCurrentCountryIso;
   private CallStateReceiver mCallStateReceiver;
   private boolean mWasEmptyBeforeTextChange;
   /**
@@ -324,8 +329,6 @@
 
     mFirstLaunch = state == null;
 
-    mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
-
     mProhibitedPhoneNumberRegexp =
         getResources().getString(R.string.config_prohibited_phone_number_regexp);
 
@@ -377,8 +380,7 @@
     mDigits.addTextChangedListener(this);
     mDigits.setElegantTextHeight(false);
 
-    initPhoneNumberFormattingTextWatcherExecutor.executeSerial(
-        GeoUtil.getCurrentCountryIso(getActivity()));
+    initPhoneNumberFormattingTextWatcherExecutor.executeSerial(getCurrentCountryIso());
 
     // Check for the presence of the keypad
     View oneButton = fragmentView.findViewById(R.id.one);
@@ -422,6 +424,19 @@
     return fragmentView;
   }
 
+  private String getCurrentCountryIso() {
+    if (currentCountryIsoForTesting.isPresent()) {
+      return currentCountryIsoForTesting.get();
+    }
+
+    return GeoUtil.getCurrentCountryIso(getActivity());
+  }
+
+  @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+  public static void setCurrentCountryIsoForTesting(String countryCode) {
+    currentCountryIsoForTesting = Optional.of(countryCode);
+  }
+
   private boolean isLayoutReady() {
     return mDigits != null;
   }
@@ -561,7 +576,7 @@
 
   /** Sets formatted digits to digits field. */
   private void setFormattedDigits(String data, String normalizedNumber) {
-    final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso);
+    final String formatted = getFormattedDigits(data, normalizedNumber, getCurrentCountryIso());
     if (!TextUtils.isEmpty(formatted)) {
       Editable digits = mDigits.getText();
       digits.replace(0, digits.length(), formatted);
@@ -1717,19 +1732,189 @@
   }
 
   /**
-   * Input: the ISO 3166-1 two letters country code of the country the user is in
+   * A worker that helps formatting the phone number as the user types it in.
    *
-   * <p>Output: PhoneNumberFormattingTextWatcher. Note: It is unusual to return a non-data value
-   * from a worker, but it is a limitation in libphonenumber API that the watcher cannot be
-   * initialized on the main thread.
+   * <p>Input: the ISO 3166-1 two-letter country code of the country the user is in.
+   *
+   * <p>Output: an instance of {@link DialerPhoneNumberFormattingTextWatcher}. Note: It is unusual
+   * to return a non-data value from a worker. But {@link DialerPhoneNumberFormattingTextWatcher}
+   * depends on libphonenumber API, which cannot be initialized on the main thread.
    */
   private static class InitPhoneNumberFormattingTextWatcherWorker
-      implements Worker<String, PhoneNumberFormattingTextWatcher> {
+      implements Worker<String, DialerPhoneNumberFormattingTextWatcher> {
 
     @Nullable
     @Override
-    public PhoneNumberFormattingTextWatcher doInBackground(@Nullable String countryCode) {
-      return new PhoneNumberFormattingTextWatcher(countryCode);
+    public DialerPhoneNumberFormattingTextWatcher doInBackground(@Nullable String countryCode) {
+      return new DialerPhoneNumberFormattingTextWatcher(countryCode);
+    }
+  }
+
+  /**
+   * An extension of Android telephony's {@link PhoneNumberFormattingTextWatcher}. This watcher
+   * skips formatting Argentina mobile numbers for domestic calls.
+   *
+   * <p>As of Nov. 28, 2017, the as-you-type-formatting provided by libphonenumber's
+   * AsYouTypeFormatter (which {@link PhoneNumberFormattingTextWatcher} depends on) can't correctly
+   * format Argentina mobile numbers for domestic calls (a bug). We temporarily disable the
+   * formatting for such numbers until libphonenumber is fixed (which will come as early as the next
+   * Android release).
+   */
+  @VisibleForTesting
+  public static class DialerPhoneNumberFormattingTextWatcher
+      extends PhoneNumberFormattingTextWatcher {
+    private static final Pattern AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN;
+
+    // This static initialization block builds a pattern for domestic calls to Argentina mobile
+    // numbers:
+    // (1) Local calls: 15 <local number>
+    // (2) Long distance calls: <area code> 15 <local number>
+    // See https://en.wikipedia.org/wiki/Telephone_numbers_in_Argentina for detailed explanations.
+    static {
+      String regex =
+          "0?("
+              + "  ("
+              + "   11|"
+              + "   2("
+              + "     2("
+              + "       02?|"
+              + "       [13]|"
+              + "       2[13-79]|"
+              + "       4[1-6]|"
+              + "       5[2457]|"
+              + "       6[124-8]|"
+              + "       7[1-4]|"
+              + "       8[13-6]|"
+              + "       9[1267]"
+              + "     )|"
+              + "     3("
+              + "       02?|"
+              + "       1[467]|"
+              + "       2[03-6]|"
+              + "       3[13-8]|"
+              + "       [49][2-6]|"
+              + "       5[2-8]|"
+              + "       [67]"
+              + "     )|"
+              + "     4("
+              + "       7[3-578]|"
+              + "       9"
+              + "     )|"
+              + "     6("
+              + "       [0136]|"
+              + "       2[24-6]|"
+              + "       4[6-8]?|"
+              + "       5[15-8]"
+              + "     )|"
+              + "     80|"
+              + "     9("
+              + "       0[1-3]|"
+              + "       [19]|"
+              + "       2\\d|"
+              + "       3[1-6]|"
+              + "       4[02568]?|"
+              + "       5[2-4]|"
+              + "       6[2-46]|"
+              + "       72?|"
+              + "       8[23]?"
+              + "     )"
+              + "   )|"
+              + "   3("
+              + "     3("
+              + "       2[79]|"
+              + "       6|"
+              + "       8[2578]"
+              + "     )|"
+              + "     4("
+              + "       0[0-24-9]|"
+              + "       [12]|"
+              + "       3[5-8]?|"
+              + "       4[24-7]|"
+              + "       5[4-68]?|"
+              + "       6[02-9]|"
+              + "       7[126]|"
+              + "       8[2379]?|"
+              + "       9[1-36-8]"
+              + "     )|"
+              + "     5("
+              + "       1|"
+              + "       2[1245]|"
+              + "       3[237]?|"
+              + "       4[1-46-9]|"
+              + "       6[2-4]|"
+              + "       7[1-6]|"
+              + "       8[2-5]?"
+              + "     )|"
+              + "     6[24]|"
+              + "     7("
+              + "       [069]|"
+              + "       1[1568]|"
+              + "       2[15]|"
+              + "       3[145]|"
+              + "       4[13]|"
+              + "       5[14-8]|"
+              + "       7[2-57]|"
+              + "       8[126]"
+              + "     )|"
+              + "     8("
+              + "       [01]|"
+              + "       2[15-7]|"
+              + "       3[2578]?|"
+              + "       4[13-6]|"
+              + "       5[4-8]?|"
+              + "       6[1-357-9]|"
+              + "       7[36-8]?|"
+              + "       8[5-8]?|"
+              + "       9[124]"
+              + "     )"
+              + "   )"
+              + " )?15"
+              + ").*";
+      AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN = Pattern.compile(regex.replaceAll("\\s+", ""));
+    }
+
+    private final String countryCode;
+
+    DialerPhoneNumberFormattingTextWatcher(String countryCode) {
+      super(countryCode);
+      this.countryCode = countryCode;
+    }
+
+    @Override
+    public synchronized void afterTextChanged(Editable s) {
+      super.afterTextChanged(s);
+
+      if (!"AR".equals(countryCode)) {
+        return;
+      }
+
+      String rawNumber = getRawNumber(s);
+
+      // As modifying the input will trigger another call to afterTextChanged(Editable), we must
+      // check whether the input's format has already been removed and return if it has
+      // been to avoid infinite recursion.
+      if (rawNumber.contentEquals(s)) {
+        return;
+      }
+
+      Matcher matcher = AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN.matcher(rawNumber);
+      if (matcher.matches()) {
+        s.replace(0, s.length(), rawNumber);
+        PhoneNumberUtils.addTtsSpan(s, 0 /* start */, s.length() /* endExclusive */);
+      }
+    }
+
+    private static String getRawNumber(Editable s) {
+      StringBuilder rawNumberBuilder = new StringBuilder();
+
+      for (int i = 0; i < s.length(); i++) {
+        char c = s.charAt(i);
+        if (PhoneNumberUtils.isNonSeparator(c)) {
+          rawNumberBuilder.append(c);
+        }
+      }
+
+      return rawNumberBuilder.toString();
     }
   }
 }