Merge "Override bidi property of new emojis" into oc-dev
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index d84502f..f65f397 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -592,10 +592,21 @@
         static {
             DIR_TYPE_CACHE = new byte[DIR_TYPE_CACHE_SIZE];
             for (int i = 0; i < DIR_TYPE_CACHE_SIZE; i++) {
+                // Calling Character.getDirectionality() is OK here, since new emojis start after
+                // the end of our cache.
                 DIR_TYPE_CACHE[i] = Character.getDirectionality(i);
             }
         }
 
+        private static byte getDirectionality(int codePoint) {
+            if (Emoji.isNewEmoji(codePoint)) {
+                // TODO: Fix or remove once emoji-data.text 5.0 is in ICU or update to 6.0.
+                return Character.DIRECTIONALITY_OTHER_NEUTRALS;
+            } else {
+                return Character.getDirectionality(codePoint);
+            }
+        }
+
         // Internal instance variables.
 
         /**
@@ -809,7 +820,7 @@
          * cache.
          */
         private static byte getCachedDirectionality(char c) {
-            return c < DIR_TYPE_CACHE_SIZE ? DIR_TYPE_CACHE[c] : Character.getDirectionality(c);
+            return c < DIR_TYPE_CACHE_SIZE ? DIR_TYPE_CACHE[c] : getDirectionality(c);
         }
 
         /**
@@ -826,7 +837,7 @@
             if (Character.isHighSurrogate(lastChar)) {
                 int codePoint = Character.codePointAt(text, charIndex);
                 charIndex += Character.charCount(codePoint);
-                return Character.getDirectionality(codePoint);
+                return getDirectionality(codePoint);
             }
             charIndex++;
             byte dirType = getCachedDirectionality(lastChar);
@@ -856,7 +867,7 @@
             if (Character.isLowSurrogate(lastChar)) {
                 int codePoint = Character.codePointBefore(text, charIndex);
                 charIndex -= Character.charCount(codePoint);
-                return Character.getDirectionality(codePoint);
+                return getDirectionality(codePoint);
             }
             charIndex--;
             byte dirType = getCachedDirectionality(lastChar);
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index ee016c1..d33aad9 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -65,22 +65,32 @@
         return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER_BASE);
     }
 
-    // Returns true if the character has Emoji property.
-    public static boolean isEmoji(int codePoint) {
+    /**
+     * Returns true if the character is a new emoji still not supported in our version of ICU.
+     */
+    public static boolean isNewEmoji(int codePoint) {
         // Emoji characters new in Unicode emoji 5.0.
         // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
         // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
-        if ((0x1F6F7 <= codePoint && codePoint <= 0x1F6F8)
+        if (codePoint < 0x1F6F7 || codePoint > 0x1F9E6) {
+            // Optimization for characters outside the new emoji range.
+            return false;
+        }
+        return (0x1F6F7 <= codePoint && codePoint <= 0x1F6F8)
                 || codePoint == 0x1F91F
                 || (0x1F928 <= codePoint && codePoint <= 0x1F92F)
                 || (0x1F931 <= codePoint && codePoint <= 0x1F932)
                 || codePoint == 0x1F94C
                 || (0x1F95F <= codePoint && codePoint <= 0x1F96B)
                 || (0x1F992 <= codePoint && codePoint <= 0x1F997)
-                || (0x1F9D0 <= codePoint && codePoint <= 0x1F9E6)) {
-            return true;
-        }
-        return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI);
+                || (0x1F9D0 <= codePoint && codePoint <= 0x1F9E6);
+    }
+
+    /**
+     * Returns true if the character has Emoji property.
+     */
+    public static boolean isEmoji(int codePoint) {
+        return isNewEmoji(codePoint) || UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI);
     }
 
     // Returns true if the character can be a base character of COMBINING ENCLOSING KEYCAP.
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 2a3f036..d744b7c 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -22,6 +22,7 @@
 #include "utils/misc.h"
 #include "utils/Log.h"
 #include "unicode/ubidi.h"
+#include <minikin/Emoji.h>
 
 namespace android {
 
@@ -38,6 +39,9 @@
         if (info != NULL) {
             UErrorCode status = U_ZERO_ERROR;
             UBiDi* bidi = ubidi_openSized(n, 0, &status);
+            // Set callbacks to override bidi classes of new emoji
+            ubidi_setClassCallback(
+                    bidi, minikin::emojiBidiOverride, nullptr, nullptr, nullptr, &status);
             ubidi_setPara(bidi, chs, n, dir, NULL, &status);
             if (U_SUCCESS(status)) {
                 for (int i = 0; i < n; ++i) {