diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java index d84502f690356..f65f39762b75e 100644 --- a/core/java/android/text/BidiFormatter.java +++ b/core/java/android/text/BidiFormatter.java @@ -592,10 +592,21 @@ public final class BidiFormatter { 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 @@ public final class BidiFormatter { * 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 @@ public final class BidiFormatter { 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 @@ public final class BidiFormatter { 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 ee016c1d43e7d..d33aad99e1a56 100644 --- a/core/java/android/text/Emoji.java +++ b/core/java/android/text/Emoji.java @@ -65,22 +65,32 @@ public class Emoji { 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 2a3f0361a9cd4..d744b7c989ea6 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 namespace android { @@ -38,6 +39,9 @@ static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, 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) {