Merge "Add two convenient utility methods for L new API" into lmp-dev
diff --git a/java/res/values-land/config.xml b/java/res/values-land/config.xml
index 9ee4300..5eea4c1 100644
--- a/java/res/values-land/config.xml
+++ b/java/res/values-land/config.xml
@@ -49,8 +49,6 @@
     <fraction name="config_key_hint_label_ratio">52%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">40%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">40.000%</fraction>
-    <!-- left or right padding of label alignment -->
-    <dimen name="config_key_label_horizontal_padding">8dp</dimen>
 
     <!-- For 5-row keyboard -->
     <fraction name="config_key_vertical_gap_5row">3.20%p</fraction>
diff --git a/java/res/values-sw600dp-land/config.xml b/java/res/values-sw600dp-land/config.xml
index ba8b52f..6368eef 100644
--- a/java/res/values-sw600dp-land/config.xml
+++ b/java/res/values-sw600dp-land/config.xml
@@ -39,8 +39,6 @@
     <fraction name="config_key_hint_label_ratio">34%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">29%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">30.0%</fraction>
-    <!-- left or right padding of label alignment -->
-    <dimen name="config_key_label_horizontal_padding">18dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">4dp</dimen>
 
     <!-- For 5-row keyboard -->
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index d97538d..9d16e2c 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -52,8 +52,6 @@
     <fraction name="config_key_hint_label_ratio">28%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">22%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">28.0%</fraction>
-    <!-- left or right padding of label alignment -->
-    <dimen name="config_key_label_horizontal_padding">6dp</dimen>
     <dimen name="config_key_hint_letter_padding">3dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen>
 
diff --git a/java/res/values-sw768dp-land/config.xml b/java/res/values-sw768dp-land/config.xml
index 63f86ba..a1659b4 100644
--- a/java/res/values-sw768dp-land/config.xml
+++ b/java/res/values-sw768dp-land/config.xml
@@ -41,8 +41,6 @@
     <fraction name="config_key_hint_label_ratio">28%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">24%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">24.00%</fraction>
-    <!-- left or right padding of label alignment -->
-    <dimen name="config_key_label_horizontal_padding">18dp</dimen>
 
     <!-- For 5-row keyboard -->
     <fraction name="config_key_vertical_gap_5row">2.65%p</fraction>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 94b38d8..635061d 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -50,8 +50,6 @@
     <fraction name="config_key_hint_label_ratio">28%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">26%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">29.03%</fraction>
-    <!-- left or right padding of label alignment -->
-    <dimen name="config_key_label_horizontal_padding">6dp</dimen>
     <dimen name="config_key_hint_letter_padding">3dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen>
 
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 6487955..5434106 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -50,8 +50,6 @@
              {@link StateListDrawable}, with the following possible states: normal, pressed. -->
         <attr name="spacebarBackground" format="reference" />
         <attr name="spacebarIconWidthRatio" format="float" />
-        <!-- Horizontal padding of left/right aligned key label to the edge of the key. -->
-        <attr name="keyLabelHorizontalPadding" format="dimension" />
         <!-- Right padding of hint letter to the edge of the key.-->
         <attr name="keyHintLetterPadding" format="dimension" />
         <!-- Bottom padding of popup hint letter "..." to the edge of the key.-->
@@ -291,9 +289,6 @@
         <!-- The key label flags. -->
         <attr name="keyLabelFlags" format="integer">
             <!-- This should be aligned with Key.LABEL_FLAGS__* -->
-            <flag name="alignLeft" value="0x01" />
-            <flag name="alignRight" value="0x02" />
-            <flag name="alignButtom" value="0x04" />
             <flag name="alignLeftOfCenter" value="0x08" />
             <flag name="fontNormal" value="0x10" />
             <flag name="fontMonoSpace" value="0x20" />
@@ -306,9 +301,6 @@
             <flag name="hasPopupHint" value="0x200" />
             <flag name="hasShiftedLetterHint" value="0x400" />
             <flag name="hasHintLabel" value="0x800" />
-            <!-- These two flags are currently unused. Leave these for possible future use. -->
-            <flag name="withIconLeft" value="0x1000" />
-            <flag name="withIconRight" value="0x2000" />
             <flag name="autoXScale" value="0x4000" />
             <!-- The autoScale value implies autoXScale bit on to optimize scaling code path. -->
             <flag name="autoScale" value="0xc000" />
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 7091213..d748c91 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -54,7 +54,6 @@
     <fraction name="config_key_hint_label_ratio">44%</fraction>
     <fraction name="config_key_shifted_letter_hint_ratio">35%</fraction>
     <fraction name="config_language_on_spacebar_text_ratio">33.735%</fraction>
-    <dimen name="config_key_label_horizontal_padding">4dp</dimen>
     <dimen name="config_key_hint_letter_padding">1dp</dimen>
     <dimen name="config_key_shifted_letter_hint_padding">2dp</dimen>
 
diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml
index 2b2a80a..b139110 100644
--- a/java/res/values/themes-common.xml
+++ b/java/res/values/themes-common.xml
@@ -39,7 +39,6 @@
         <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio</item>
         <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio</item>
         <item name="keyTypeface">normal</item>
-        <item name="keyLabelHorizontalPadding">@dimen/config_key_label_horizontal_padding</item>
         <item name="keyHintLetterPadding">@dimen/config_key_hint_letter_padding</item>
         <item name="keyPopupHintLetterPadding">@dimen/config_key_popup_hint_letter_padding</item>
         <item name="keyShiftedLetterHintPadding">@dimen/config_key_shifted_letter_hint_padding</item>
diff --git a/java/res/xml-v16/keystyle_devanagari_sign_virama.xml b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml
index 0300e4a..e28ad53 100644
--- a/java/res/xml-v16/keystyle_devanagari_sign_virama.xml
+++ b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml
@@ -27,8 +27,13 @@
         <case latin:keyboardLayoutSet="hindi_compact">
             <!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
             <key-style
-                latin:styleName="moreKeysDevanagariSignVirama"
+                latin:styleName="moreKeySpecDevanagariSignVirama"
                 latin:moreKeys="&#x094D;" />
+            <!-- U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+                 U+090D: "ऍ" DEVANAGARI LETTER CANDRA E -->
+            <key-style
+                latin:styleName="moreKeysDevanagariSignVirama"
+                latin:moreKeys="&#x0945;,&#x090D;" />
         </case>
         <case latin:keyboardLayoutSet="marathi">
             <!-- U+0905: "अ" DEVANAGARI LETTER A -->
diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml
index 8a76fe3..df4448c 100644
--- a/java/res/xml/key_styles_number.xml
+++ b/java/res/xml/key_styles_number.xml
@@ -120,7 +120,6 @@
     <key-style
         latin:styleName="numSpaceKeyStyle"
         latin:keySpec="!icon/space_key_for_number_layout|!code/key_space"
-        latin:keyLabelFlags="alignButtom"
         latin:keyActionFlags="enableLongPress"
         latin:parentStyle="numKeyBaseStyle" />
     <!-- Override defaultEnterKeyStyle in key_styles_enter.xml -->
diff --git a/java/res/xml/keystyle_devanagari_sign_virama.xml b/java/res/xml/keystyle_devanagari_sign_virama.xml
index a7c5998..ff778d9 100644
--- a/java/res/xml/keystyle_devanagari_sign_virama.xml
+++ b/java/res/xml/keystyle_devanagari_sign_virama.xml
@@ -28,8 +28,14 @@
             <!-- U+25CC: "◌" DOTTED CIRCLE
                  U+094D: "्" DEVANAGARI SIGN VIRAMA -->
             <key-style
-                latin:styleName="moreKeysDevanagariSignVirama"
+                latin:styleName="moreKeySpecDevanagariSignVirama"
                 latin:moreKeys="&#x25CC;&#x094D;|&#x094D;" />
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
+                 U+090D: "ऍ" DEVANAGARI LETTER CANDRA E -->
+            <key-style
+                latin:styleName="moreKeysDevanagariSignVirama"
+                latin:moreKeys="&#x25CC;&#x0945;,&#x090D;" />
         </case>
         <case latin:keyboardLayoutSet="marathi">
             <!-- U+0905: "अ" DEVANAGARI LETTER A -->
diff --git a/java/res/xml/rowkeys_hindi_compact2.xml b/java/res/xml/rowkeys_hindi_compact2.xml
index 7cbe5ef..94aaaf2 100644
--- a/java/res/xml/rowkeys_hindi_compact2.xml
+++ b/java/res/xml/rowkeys_hindi_compact2.xml
@@ -42,7 +42,7 @@
     <!-- U+0905: "अ" DEVANAGARI LETTER A -->
     <Key
         latin:keySpec="&#x0905;"
-        latin:keyStyle="moreKeysDevanagariSignVirama" />
+        latin:keyStyle="moreKeySpecDevanagariSignVirama" />
     <!-- Because the font rendering system prior to API version 16 can't automatically
          render dotted circle for incomplete combining letter of some scripts, different
          set of Key definitions are needed based on the API version. -->
diff --git a/java/res/xml/rowkeys_hindi_compact3.xml b/java/res/xml/rowkeys_hindi_compact3.xml
index 89a9f9d..394eb23 100644
--- a/java/res/xml/rowkeys_hindi_compact3.xml
+++ b/java/res/xml/rowkeys_hindi_compact3.xml
@@ -30,11 +30,8 @@
     <!-- Because the font rendering system prior to API version 16 can't automatically
          render dotted circle for incomplete combining letter of some scripts, different
          set of Key definitions are needed based on the API version. -->
-    <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_candra_e" />
-    <!-- U+090D: "ऍ" DEVANAGARI LETTER CANDRA E -->
-    <Key
-        latin:keySpec="&#x090D;"
-        latin:keyStyle="moreKeysDevanagariVowelSignCandraE" />
+    <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+    <Key latin:keyStyle="baseKeyDevanagariSignVirama" />
     <!-- Because the font rendering system prior to API version 16 can't automatically
          render dotted circle for incomplete combining letter of some scripts, different
          set of Key definitions are needed based on the API version. -->
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 665d9f7..af54fb6 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -58,9 +58,6 @@
     private final String mHintLabel;
     /** Flags of the label */
     private final int mLabelFlags;
-    private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01;
-    private static final int LABEL_FLAGS_ALIGN_RIGHT = 0x02;
-    private static final int LABEL_FLAGS_ALIGN_BUTTOM = 0x04;
     private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08;
     // Font typeface specification.
     private static final int LABEL_FLAGS_FONT_MASK = 0x30;
@@ -78,8 +75,6 @@
     private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200;
     private static final int LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT = 0x400;
     private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800;
-    private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000;
-    private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000;
     // The bit to calculate the ratio of key label width against key width. If autoXScale bit is on
     // and autoYScale bit is off, the key label may be shrunk only for X-direction.
     // If both autoXScale and autoYScale bits are on, the key label text size may be auto scaled.
@@ -646,18 +641,6 @@
         return Typeface.DEFAULT_BOLD;
     }
 
-    public final boolean isAlignLeft() {
-        return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0;
-    }
-
-    public final boolean isAlignRight() {
-        return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0;
-    }
-
-    public final boolean isAlignButtom() {
-        return (mLabelFlags & LABEL_FLAGS_ALIGN_BUTTOM) != 0;
-    }
-
     public final boolean isAlignLeftOfCenter() {
         return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0;
     }
@@ -675,14 +658,6 @@
         return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0;
     }
 
-    public final boolean hasLabelWithIconLeft() {
-        return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0;
-    }
-
-    public final boolean hasLabelWithIconRight() {
-        return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0;
-    }
-
     public final boolean needsAutoXScale() {
         return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index c4ca1c4..f967f62 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -47,7 +47,6 @@
  * @attr ref R.styleable#KeyboardView_functionalKeyBackground
  * @attr ref R.styleable#KeyboardView_spacebarBackground
  * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio
- * @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding
  * @attr ref R.styleable#KeyboardView_keyHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding
  * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding
@@ -74,7 +73,6 @@
 public class KeyboardView extends View {
     // XML attributes
     private final KeyVisualAttributes mKeyVisualAttributes;
-    private final int mKeyLabelHorizontalPadding;
     private final float mKeyHintLetterPadding;
     private final float mKeyPopupHintLetterPadding;
     private final float mKeyShiftedLetterHintPadding;
@@ -90,11 +88,6 @@
     // HORIZONTAL ELLIPSIS "...", character for popup hint.
     private static final String POPUP_HINT_CHAR = "\u2026";
 
-    // Margin between the label and the icon on a key that has both of them.
-    // Specified by the fraction of the key width.
-    // TODO: Use resource parameter for this value.
-    private static final float LABEL_ICON_MARGIN = 0.05f;
-
     // The maximum key label width in the proportion to the key width.
     private static final float MAX_LABEL_RATIO = 0.90f;
 
@@ -137,8 +130,6 @@
         mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground;
         mSpacebarIconWidthRatio = keyboardViewAttr.getFloat(
                 R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f);
-        mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset(
-                R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
         mKeyHintLetterPadding = keyboardViewAttr.getDimension(
                 R.styleable.KeyboardView_keyHintLetterPadding, 0.0f);
         mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension(
@@ -376,27 +367,10 @@
             final float baseline = centerY + labelCharHeight / 2.0f;
 
             // Horizontal label text alignment
-            float labelWidth = 0.0f;
-            if (key.isAlignLeft()) {
-                positionX = mKeyLabelHorizontalPadding;
-                paint.setTextAlign(Align.LEFT);
-            } else if (key.isAlignRight()) {
-                positionX = keyWidth - mKeyLabelHorizontalPadding;
-                paint.setTextAlign(Align.RIGHT);
-            } else if (key.isAlignLeftOfCenter()) {
+            if (key.isAlignLeftOfCenter()) {
                 // TODO: Parameterise this?
                 positionX = centerX - labelCharWidth * 7.0f / 4.0f;
                 paint.setTextAlign(Align.LEFT);
-            } else if (key.hasLabelWithIconLeft() && icon != null) {
-                labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth()
-                        + LABEL_ICON_MARGIN * keyWidth;
-                positionX = centerX + labelWidth / 2.0f;
-                paint.setTextAlign(Align.RIGHT);
-            } else if (key.hasLabelWithIconRight() && icon != null) {
-                labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth()
-                        + LABEL_ICON_MARGIN * keyWidth;
-                positionX = centerX - labelWidth / 2.0f;
-                paint.setTextAlign(Align.LEFT);
             } else {
                 positionX = centerX;
                 paint.setTextAlign(Align.CENTER);
@@ -430,19 +404,6 @@
             // Turn off drop shadow and reset x-scale.
             paint.clearShadowLayer();
             paint.setTextScaleX(1.0f);
-
-            if (icon != null) {
-                final int iconWidth = icon.getIntrinsicWidth();
-                final int iconHeight = icon.getIntrinsicHeight();
-                final int iconY = (keyHeight - iconHeight) / 2;
-                if (key.hasLabelWithIconLeft()) {
-                    final int iconX = (int)(centerX - labelWidth / 2.0f);
-                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
-                } else if (key.hasLabelWithIconRight()) {
-                    final int iconX = (int)(centerX + labelWidth / 2.0f - iconWidth);
-                    drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
-                }
-            }
         }
 
         // Draw hint label.
@@ -493,16 +454,9 @@
                 iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
             }
             final int iconHeight = icon.getIntrinsicHeight();
-            final int iconY = key.isAlignButtom() ? keyHeight - iconHeight
-                    : (keyHeight - iconHeight) / 2;
-            final int iconX;
-            if (key.isAlignLeft()) {
-                iconX = mKeyLabelHorizontalPadding;
-            } else if (key.isAlignRight()) {
-                iconX = keyWidth - mKeyLabelHorizontalPadding - iconWidth;
-            } else { // Align center
-                iconX = (keyWidth - iconWidth) / 2;
-            }
+            // Align center.
+            final int iconY = (keyHeight - iconHeight) / 2;
+            final int iconX = (keyWidth - iconWidth) / 2;
             drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
         }
 
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index ceb0383..38f0b3f 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -18,6 +18,7 @@
 
 import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
 
+import android.text.Spanned;
 import android.text.TextUtils;
 
 import com.android.inputmethod.annotations.UsedForTesting;
@@ -26,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public final class StringUtils {
     public static final int CAPITALIZE_NONE = 0;  // No caps, or mixed case
@@ -503,6 +506,55 @@
         return lastIndex - i;
     }
 
+    /**
+     * Splits the given {@code charSequence} with at occurrences of the given {@code regex}.
+     * <p>
+     * This is equivalent to
+     * {@code charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0)}
+     * except that the spans are preserved in the result array.
+     * </p>
+     * @param input the character sequence to be split.
+     * @param regex the regex pattern to be used as the separator.
+     * @param preserveTrailingEmptySegments {@code true} to preserve the trailing empty
+     * segments. Otherwise, trailing empty segments will be removed before being returned.
+     * @return the array which contains the result. All the spans in the {@param input} is
+     * preserved.
+     */
+    @UsedForTesting
+    public static CharSequence[] split(final CharSequence charSequence, final String regex,
+            final boolean preserveTrailingEmptySegments) {
+        // A short-cut for non-spanned strings.
+        if (!(charSequence instanceof Spanned)) {
+            // -1 means that trailing empty segments will be preserved.
+            return charSequence.toString().split(regex, preserveTrailingEmptySegments ? -1 : 0);
+        }
+
+        // Hereafter, emulate String.split for CharSequence.
+        final ArrayList<CharSequence> sequences = new ArrayList<>();
+        final Matcher matcher = Pattern.compile(regex).matcher(charSequence);
+        int nextStart = 0;
+        boolean matched = false;
+        while (matcher.find()) {
+            sequences.add(charSequence.subSequence(nextStart, matcher.start()));
+            nextStart = matcher.end();
+            matched = true;
+        }
+        if (!matched) {
+            // never matched. preserveTrailingEmptySegments is ignored in this case.
+            return new CharSequence[] { charSequence };
+        }
+        sequences.add(charSequence.subSequence(nextStart, charSequence.length()));
+        if (!preserveTrailingEmptySegments) {
+            for (int i = sequences.size() - 1; i >= 0; --i) {
+                if (!TextUtils.isEmpty(sequences.get(i))) {
+                    break;
+                }
+                sequences.remove(i);
+            }
+        }
+        return sequences.toArray(new CharSequence[sequences.size()]);
+    }
+
     @UsedForTesting
     public static class Stringizer<E> {
         public String stringize(final E element) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
index a7f6823..2b625c3 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
@@ -163,9 +163,11 @@
                     // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
                     // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
                     key("\u0911", moreKey(VOWEL_SIGN_CANDRA_O, "\u0949")),
-                    // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+                    // U+094D: "्" DEVANAGARI SIGN VIRAMA
                     // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E
-                    key("\u090D", moreKey(VOWEL_SIGN_CANDRA_E, "\u0945")),
+                    // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+                    key(SIGN_VIRAMA, "\u094D", joinMoreKeys(
+                            moreKey(VOWEL_SIGN_CANDRA_E, "\u0945"), "\u090D")),
                     // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
                     // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
                     // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index 4448a6b..637ae10 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -18,6 +18,9 @@
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
 
 import com.android.inputmethod.latin.Constants;
 
@@ -326,4 +329,171 @@
         assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'word'"));
         assertEquals(0, StringUtils.getTrailingSingleQuotesCount("I'm"));
     }
+
+    private static void assertSpanCount(final int expectedCount, final CharSequence cs) {
+        final int actualCount;
+        if (cs instanceof Spanned) {
+            final Spanned spanned = (Spanned) cs;
+            actualCount = spanned.getSpans(0, spanned.length(), Object.class).length;
+        } else {
+            actualCount = 0;
+        }
+        assertEquals(expectedCount, actualCount);
+    }
+
+    private static void assertSpan(final CharSequence cs, final Object expectedSpan,
+            final int expectedStart, final int expectedEnd, final int expectedFlags) {
+        assertTrue(cs instanceof Spanned);
+        final Spanned spanned = (Spanned) cs;
+        final Object[] actualSpans = spanned.getSpans(0, spanned.length(), Object.class);
+        for (Object actualSpan : actualSpans) {
+            if (actualSpan == expectedSpan) {
+                final int actualStart = spanned.getSpanStart(actualSpan);
+                final int actualEnd = spanned.getSpanEnd(actualSpan);
+                final int actualFlags = spanned.getSpanFlags(actualSpan);
+                assertEquals(expectedStart, actualStart);
+                assertEquals(expectedEnd, actualEnd);
+                assertEquals(expectedFlags, actualFlags);
+                return;
+            }
+        }
+        assertTrue(false);
+    }
+
+    public void testSplitCharSequenceWithSpan() {
+        // text:  " a bcd efg hij  "
+        // span1:  ^^^^^^^
+        // span2:  ^^^^^
+        // span3:              ^
+        final SpannableString spannableString = new SpannableString(" a bcd efg hij  ");
+        final Object span1 = new Object();
+        final Object span2 = new Object();
+        final Object span3 = new Object();
+        final int SPAN1_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+        final int SPAN2_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
+        final int SPAN3_FLAGS = Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+        spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS);
+        spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS);
+        spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS);
+        final CharSequence[] charSequencesFromSpanned = StringUtils.split(
+                spannableString, " ", true /* preserveTrailingEmptySegmengs */);
+        final CharSequence[] charSequencesFromString = StringUtils.split(
+                spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */);
+
+
+        assertEquals(7, charSequencesFromString.length);
+        assertEquals(7, charSequencesFromSpanned.length);
+
+        // text:  ""
+        // span1: ^
+        // span2: ^
+        // span3:
+        assertEquals("", charSequencesFromString[0].toString());
+        assertSpanCount(0, charSequencesFromString[0]);
+        assertEquals("", charSequencesFromSpanned[0].toString());
+        assertSpanCount(2, charSequencesFromSpanned[0]);
+        assertSpan(charSequencesFromSpanned[0], span1, 0, 0, SPAN1_FLAGS);
+        assertSpan(charSequencesFromSpanned[0], span2, 0, 0, SPAN2_FLAGS);
+
+        // text:  "a"
+        // span1:  ^
+        // span2:  ^
+        // span3:
+        assertEquals("a", charSequencesFromString[1].toString());
+        assertSpanCount(0, charSequencesFromString[1]);
+        assertEquals("a", charSequencesFromSpanned[1].toString());
+        assertSpanCount(2, charSequencesFromSpanned[1]);
+        assertSpan(charSequencesFromSpanned[1], span1, 0, 1, SPAN1_FLAGS);
+        assertSpan(charSequencesFromSpanned[1], span2, 0, 1, SPAN2_FLAGS);
+
+        // text:  "bcd"
+        // span1:  ^^^
+        // span2:  ^^
+        // span3:
+        assertEquals("bcd", charSequencesFromString[2].toString());
+        assertSpanCount(0, charSequencesFromString[2]);
+        assertEquals("bcd", charSequencesFromSpanned[2].toString());
+        assertSpanCount(2, charSequencesFromSpanned[2]);
+        assertSpan(charSequencesFromSpanned[2], span1, 0, 3, SPAN1_FLAGS);
+        assertSpan(charSequencesFromSpanned[2], span2, 0, 2, SPAN2_FLAGS);
+
+        // text:  "efg"
+        // span1:
+        // span2:
+        // span3:
+        assertEquals("efg", charSequencesFromString[3].toString());
+        assertSpanCount(0, charSequencesFromString[3]);
+        assertEquals("efg", charSequencesFromSpanned[3].toString());
+        assertSpanCount(0, charSequencesFromSpanned[3]);
+
+        // text:  "hij"
+        // span1:
+        // span2:
+        // span3:   ^
+        assertEquals("hij", charSequencesFromString[4].toString());
+        assertSpanCount(0, charSequencesFromString[4]);
+        assertEquals("hij", charSequencesFromSpanned[4].toString());
+        assertSpanCount(1, charSequencesFromSpanned[4]);
+        assertSpan(charSequencesFromSpanned[4], span3, 1, 2, SPAN3_FLAGS);
+
+        // text:  ""
+        // span1:
+        // span2:
+        // span3:
+        assertEquals("", charSequencesFromString[5].toString());
+        assertSpanCount(0, charSequencesFromString[5]);
+        assertEquals("", charSequencesFromSpanned[5].toString());
+        assertSpanCount(0, charSequencesFromSpanned[5]);
+
+        // text:  ""
+        // span1:
+        // span2:
+        // span3:
+        assertEquals("", charSequencesFromString[6].toString());
+        assertSpanCount(0, charSequencesFromString[6]);
+        assertEquals("", charSequencesFromSpanned[6].toString());
+        assertSpanCount(0, charSequencesFromSpanned[6]);
+    }
+
+    public void testSplitCharSequencePreserveTrailingEmptySegmengs() {
+        assertEquals(1, StringUtils.split("", " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(1, StringUtils.split(new SpannedString(""), " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(1, StringUtils.split("", " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(1, StringUtils.split(new SpannedString(""), " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(0, StringUtils.split(" ", " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(0, StringUtils.split(new SpannedString(" "), " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(2, StringUtils.split(" ", " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(2, StringUtils.split(new SpannedString(" "), " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(3, StringUtils.split("a b c  ", " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(3, StringUtils.split(new SpannedString("a b c  "), " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(5, StringUtils.split("a b c  ", " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(5, StringUtils.split(new SpannedString("a b c  "), " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(6, StringUtils.split("a     b ", " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(6, StringUtils.split(new SpannedString("a     b "), " ",
+                false /* preserveTrailingEmptySegmengs */).length);
+
+        assertEquals(7, StringUtils.split("a     b ", " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+        assertEquals(7, StringUtils.split(new SpannedString("a     b "), " ",
+                true /* preserveTrailingEmptySegmengs */).length);
+    }
 }