Add digit grouping to display

Bug: 27461988

Add digit grouping separators to formula and result.

The result display piece of this is an annoyingly complex change,
since it interacts with our already subtle calculations of the
number of digits to display, our initial evaluation precision,
etc.

We display grouping separators in the result only when the entire
whole part of the number, with grouping separators, fits into
the display.  If it fits into the display without, but not with,
grouping separators, we force scientific notation. This may require
the result to be scrollable when it otherwise would not be, and
leads to an interesting set of corner cases, which we claim to
handle reasonably.

Some cleanups were applied to make this easier, more useful, and
more debuggable.  These included:

More accurate bookkeeping about different character widths. Otherwise
scrolling with grouping separators was not smooth.

Ignore grouping separators and spaces on input, as we should have
been doing all along.

Only redisplay the result if the character (as opposed to pixel)
position changed. This makes up for some extra computation and
facilitates debugging.

Introduce StringUtils.java to hold digit string operations that really
don't fit anywhere else.  Move the duplicated repeat() function there.

Change-Id: I00502b9906b184671cd3379cd68b0447939b2394
diff --git a/src/com/android/calculator2/CalculatorText.java b/src/com/android/calculator2/CalculatorText.java
index 52006ea..73df9ed 100644
--- a/src/com/android/calculator2/CalculatorText.java
+++ b/src/com/android/calculator2/CalculatorText.java
@@ -182,13 +182,13 @@
      */
     public void changeTextTo(CharSequence newText) {
         final CharSequence oldText = getText();
-        if (startsWith(newText, oldText)) {
-            final int newLen = newText.length();
-            final int oldLen = oldText.length();
-            if (newLen == oldLen + 1) {
+        final char separator = KeyMaps.translateResult(",").charAt(0);
+        final CharSequence added = StringUtils.getExtensionIgnoring(newText, oldText, separator);
+        if (added != null) {
+            if (added.length() == 1) {
                 // The algorithm for pronouncing a single character doesn't seem
                 // to respect our hints.  Don't give it the choice.
-                final char c = newText.charAt(oldLen);
+                final char c = added.charAt(0);
                 final int id = KeyMaps.keyForChar(c);
                 final String descr = KeyMaps.toDescriptiveString(getContext(), id);
                 if (descr != null) {
@@ -196,8 +196,8 @@
                 } else {
                     announceForAccessibility(String.valueOf(c));
                 }
-            } else if (newLen > oldLen) {
-                announceForAccessibility(newText.subSequence(oldLen, newLen));
+            } else if (added.length() != 0) {
+                announceForAccessibility(added);
             }
         } else {
             announceForAccessibility(newText);