Add API to access ICU's East Asian Width

Currently there is no way for an application built against the API to
access East Asian Width data from ICU. This adds an API for applications
to use to access it for correct drawing of international characters.

Change-Id: Iab50698ee555ae2ca8ab4b242cc14aa6e0dc3b48
diff --git a/api/current.xml b/api/current.xml
index 91344d6..9c663fe 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -147852,6 +147852,38 @@
 <parameter name="count" type="int">
 </parameter>
 </method>
+<method name="getEastAsianWidth"
+ return="int"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="input" type="char">
+</parameter>
+</method>
+<method name="getEastAsianWidths"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="src" type="char[]">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="count" type="int">
+</parameter>
+<parameter name="dest" type="byte[]">
+</parameter>
+</method>
 <method name="getMirror"
  return="char"
  abstract="false"
@@ -147882,6 +147914,72 @@
 <parameter name="count" type="int">
 </parameter>
 </method>
+<field name="EAST_ASIAN_WIDTH_AMBIGUOUS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EAST_ASIAN_WIDTH_FULL_WIDTH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EAST_ASIAN_WIDTH_HALF_WIDTH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EAST_ASIAN_WIDTH_NARROW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EAST_ASIAN_WIDTH_NEUTRAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EAST_ASIAN_WIDTH_WIDE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="Annotation"
  extends="java.lang.Object"
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index 6dfd64d..af93b5d 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -22,6 +22,13 @@
  */
 public class AndroidCharacter
 {
+    public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0;
+    public static final int EAST_ASIAN_WIDTH_AMBIGUOUS = 1;
+    public static final int EAST_ASIAN_WIDTH_HALF_WIDTH = 2;
+    public static final int EAST_ASIAN_WIDTH_FULL_WIDTH = 3;
+    public static final int EAST_ASIAN_WIDTH_NARROW = 4;
+    public static final int EAST_ASIAN_WIDTH_WIDE = 5;
+
     /**
      * Fill in the first <code>count</code> bytes of <code>dest</code> with the
      * directionalities from the first <code>count</code> chars of <code>src</code>.
@@ -30,6 +37,38 @@
      */
     public native static void getDirectionalities(char[] src, byte[] dest,
                                                   int count);
+
+    /**
+     * Calculate the East Asian Width of a character according to
+     * <a href="http://unicode.org/reports/tr11/">Unicode TR#11</a>. The return
+     * will be one of {@link #EAST_ASIAN_WIDTH_NEUTRAL},
+     * {@link #EAST_ASIAN_WIDTH_AMBIGUOUS}, {@link #EAST_ASIAN_WIDTH_HALF_WIDTH},
+     * {@link #EAST_ASIAN_WIDTH_FULL_WIDTH}, {@link #EAST_ASIAN_WIDTH_NARROW},
+     * or {@link #EAST_ASIAN_WIDTH_WIDE}.
+     * 
+     * @param input the character to measure
+     * @return the East Asian Width for input
+     */
+    public native static int getEastAsianWidth(char input);
+
+    /**
+     * Fill the first <code>count</code> bytes of <code>dest</code> with the
+     * East Asian Width from the first <code>count</code> chars of
+     * <code>src</code>. East Asian Width is calculated based on
+     * <a href="http://unicode.org/reports/tr11/">Unicode TR#11</a>. Each entry
+     * in <code>dest> will be one of {@link #EAST_ASIAN_WIDTH_NEUTRAL},
+     * {@link #EAST_ASIAN_WIDTH_AMBIGUOUS}, {@link #EAST_ASIAN_WIDTH_HALF_WIDTH},
+     * {@link #EAST_ASIAN_WIDTH_FULL_WIDTH}, {@link #EAST_ASIAN_WIDTH_NARROW},
+     * or {@link #EAST_ASIAN_WIDTH_WIDE}.
+     * 
+     * @param src character array of input to measure
+     * @param start first character in array to measure
+     * @param count maximum number of characters to measure
+     * @param dest byte array of results for each character in src
+     */
+    public native static void getEastAsianWidths(char[] src, int start,
+                                                 int count, byte[] dest);
+
     /**
      * Replace the specified slice of <code>text</code> with the chars'
      * right-to-left mirrors (if any), returning true if any
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 05d7b73..1353478 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -23,7 +23,8 @@
 #include "utils/Log.h"
 #include "unicode/uchar.h"
 
-#define DIRECTIONALITY_UNDEFINED (-1)
+#define PROPERTY_UNDEFINED (-1)
+
 // ICU => JDK mapping
 static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
     0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0)
@@ -79,7 +80,7 @@
                                  (src[i + 1] & 0x3FF);
             int dir = u_charDirection(c);
             if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
-                dir = DIRECTIONALITY_UNDEFINED;
+                dir = PROPERTY_UNDEFINED;
             else
                 dir = directionality_map[dir];
 
@@ -89,7 +90,7 @@
             int c = src[i];
             int dir = u_charDirection(c);
             if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
-                dest[i] = DIRECTIONALITY_UNDEFINED;
+                dest[i] = PROPERTY_UNDEFINED;
             else
                 dest[i] = directionality_map[dir];
         }
@@ -100,6 +101,60 @@
     env->ReleaseByteArrayElements(destArray, dest, JNI_ABORT);
 }
 
+static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
+{
+    int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH);
+    if (width < 0 || width >= U_EA_COUNT)
+        width = PROPERTY_UNDEFINED;
+
+    return width;
+}
+
+static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
+                               int start, int count, jbyteArray destArray)
+{
+    jchar* src = env->GetCharArrayElements(srcArray, NULL);
+    jbyte* dest = env->GetByteArrayElements(destArray, NULL);
+    if (src == NULL || dest == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        goto EA_END;
+    }
+
+    if (start < 0 || start > start + count
+            || env->GetArrayLength(srcArray) < (start + count)
+            || env->GetArrayLength(destArray) < count) {
+        jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
+        goto EA_END;
+    }
+
+    for (int i = 0; i < count; i++) {
+        const int srci = start + i;
+        if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF &&
+            i + 1 < count &&
+            src[srci + 1] >= 0xDC00 && src[srci + 1] <= 0xDFFF) {
+            int c = 0x00010000 + ((src[srci] - 0xD800) << 10) +
+                                 (src[srci + 1] & 0x3FF);
+            int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
+            if (width < 0 || width >= U_EA_COUNT)
+                width = PROPERTY_UNDEFINED;
+
+            dest[i++] = width;
+            dest[i] = width;
+        } else {
+            int c = src[srci];
+            int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
+            if (width < 0 || width >= U_EA_COUNT)
+                width = PROPERTY_UNDEFINED;
+
+            dest[i] = width;
+        }
+    }
+
+EA_END:
+    env->ReleaseCharArrayElements(srcArray, src, JNI_ABORT);
+    env->ReleaseByteArrayElements(destArray, dest, JNI_ABORT);
+}
+
 static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start, int count)
 {
     jchar* data = env->GetCharArrayElements(charArray, NULL);
@@ -140,6 +195,10 @@
 static JNINativeMethod gMethods[] = {
 	{ "getDirectionalities", "([C[BI)V",
         (void*) getDirectionalities },
+	{ "getEastAsianWidth", "(C)I",
+        (void*) getEastAsianWidth },
+	{ "getEastAsianWidths", "([CII[B)V",
+        (void*) getEastAsianWidths },
 	{ "mirror", "([CII)Z",
         (void*) mirror },
 	{ "getMirror", "(C)C",