Loading core/java/android/text/AndroidBidi.java +2 −32 Original line number Original line Diff line number Diff line Loading @@ -17,11 +17,7 @@ package android.text; package android.text; import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage; import android.icu.lang.UCharacter; import android.icu.lang.UCharacterDirection; import android.icu.lang.UProperty; import android.icu.text.Bidi; import android.icu.text.Bidi; import android.icu.text.BidiClassifier; import android.text.Layout.Directions; import android.text.Layout.Directions; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading @@ -33,32 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class AndroidBidi { public class AndroidBidi { /** * Overrides ICU {@link BidiClassifier} in order to correctly handle character directions for * newest emoji that ICU is not aware of. */ public static class EmojiBidiOverride extends BidiClassifier { public EmojiBidiOverride() { super(null /* No persisting object needed */); } // Tells ICU to use the standard Unicode value. private static final int NO_OVERRIDE = UCharacter.getIntPropertyMaxValue(UProperty.BIDI_CLASS) + 1; @Override public int classify(int c) { if (Emoji.isNewEmoji(c)) { // All new emoji characters in Unicode 10.0 are of the bidi class ON. return UCharacterDirection.OTHER_NEUTRAL; } else { return NO_OVERRIDE; } } } private static final EmojiBidiOverride sEmojiBidiOverride = new EmojiBidiOverride(); /** /** * Runs the bidi algorithm on input text. * Runs the bidi algorithm on input text. */ */ Loading @@ -82,7 +52,6 @@ public class AndroidBidi { default: paraLevel = Bidi.LTR; break; default: paraLevel = Bidi.LTR; break; } } final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */); final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */); icuBidi.setCustomClassifier(sEmojiBidiOverride); icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */); icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */); for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) { chInfo[i] = icuBidi.getLevelAt(i); chInfo[i] = icuBidi.getLevelAt(i); Loading Loading @@ -221,3 +190,4 @@ public class AndroidBidi { return new Directions(ld); return new Directions(ld); } } } } core/java/android/text/BidiFormatter.java +2 −7 Original line number Original line Diff line number Diff line Loading @@ -604,16 +604,11 @@ public final class BidiFormatter { /** /** * Return Character directionality. Same as {@link Character#getDirectionality(int)} except * Return Character directionality. Same as {@link Character#getDirectionality(int)} except * it overrides values for newest emoji that are not covered by ICU. * it can override values for newest emoji that are not covered by ICU. */ */ public static byte getDirectionality(int codePoint) { public 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); return Character.getDirectionality(codePoint); } } } // Internal instance variables. // Internal instance variables. Loading core/java/android/text/Emoji.java +3 −27 Original line number Original line Diff line number Diff line Loading @@ -60,40 +60,16 @@ public class Emoji { if (c == 0x1F91D || c == 0x1F93C) { if (c == 0x1F91D || c == 0x1F93C) { return true; return true; } } // Emoji Modifier Base characters new in Unicode emoji 11 // If Android's copy of ICU is behind, check for new codepoints here. // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt // Consult log for implementation pattern. // TODO: Remove once emoji-data.text 11 is in ICU or update to 11. if ((0x1F9B5 <= c && c <= 0x1F9B6) || (0x1F9B8 <= c && c <= 0x1F9B9)) { return true; } return UCharacter.hasBinaryProperty(c, UProperty.EMOJI_MODIFIER_BASE); return UCharacter.hasBinaryProperty(c, UProperty.EMOJI_MODIFIER_BASE); } } /** * Returns true if the character is a new emoji still not supported in our version of ICU. */ public static boolean isNewEmoji(int c) { // Emoji characters new in Unicode emoji 12 // From https://www.unicode.org/Public/emoji/12.0/emoji-data.txt // TODO: Remove once emoji-data.text 12 is in ICU or update to 12. if (c < 0x1F6D5 || c > 0x1FA95) { // Optimization for characters outside the new emoji range. return false; } return c == 0x1F6D5 || c == 0x1F6FA || c == 0x1F93F || c == 0x1F971 || c == 0x1F97B || (0x1F7E0 <= c && c <= 0x1F7EB) || (0x1F90D <= c && c <= 0x1F90F) || (0x1F9A5 <= c && c <= 0x1F9AA) || (0x1F9AE <= c && c <= 0x1F9AF) || (0x1F9BA <= c && c <= 0x1F9BF) || (0x1F9C3 <= c && c <= 0x1F9CA) || (0x1F9CD <= c && c <= 0x1F9CF) || (0x1FA70 <= c && c <= 0x1FA73) || (0x1FA78 <= c && c <= 0x1FA7A) || (0x1FA80 <= c && c <= 0x1FA82) || (0x1FA90 <= c && c <= 0x1FA95); } /** /** * Returns true if the character has Emoji property. * Returns true if the character has Emoji property. */ */ public static boolean isEmoji(int codePoint) { public static boolean isEmoji(int codePoint) { return isNewEmoji(codePoint) || UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI); return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI); } } // Returns true if the character can be a base character of COMBINING ENCLOSING KEYCAP. // Returns true if the character can be a base character of COMBINING ENCLOSING KEYCAP. Loading core/tests/coretests/src/android/text/EmojiTest.java +26 −2 Original line number Original line Diff line number Diff line Loading @@ -83,12 +83,27 @@ public class EmojiTest { {0x1FA70, 0x1FA73}, {0x1FA70, 0x1FA73}, {0x1FA78, 0x1FA7A}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA82}, {0x1FA80, 0x1FA82}, {0x1FA90, 0x1FA95} {0x1FA90, 0x1FA95}, }, { // EMOJI 13 {0x1F6D6, 0x1F6D7}, {0x1F6FB, 0x1F6FC}, {0x1F90C, 0x1F90C}, {0x1F972, 0x1F972}, {0x1F977, 0x1F978}, {0x1F9A3, 0x1F9A4}, {0x1F9AB, 0x1F9AD}, {0x1F9CB, 0x1F9CB}, {0x1FA74, 0x1FA74}, {0x1FA83, 0x1FA86}, {0x1FA96, 0x1FAA8}, {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, } } }; }; final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */); final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */); icuBidi.setCustomClassifier(new AndroidBidi.EmojiBidiOverride()); for (int version = 0; version < data.length; version++) { for (int version = 0; version < data.length; version++) { for (int row = 0; row < data[version].length; row++) { for (int row = 0; row < data[version].length; row++) { Loading Loading @@ -125,6 +140,15 @@ public class EmojiTest { { // EMOJI 11 { // EMOJI 11 {0x1F9B5, 0x1F9B6}, {0x1F9B5, 0x1F9B6}, {0x1F9B8, 0x1F9B9} {0x1F9B8, 0x1F9B9} }, { // EMOJI 12 {0x1F90F, 0x1F90F}, {0x1F9BB, 0x1F9BB}, {0x1F9CD, 0x1F9CF}, }, { // EMOJI 13 {0x1F90C, 0x1F90C}, {0x1F977, 0x1F977} } } }; }; for (int version = 0; version < data.length; version++) { for (int version = 0; version < data.length; version++) { Loading Loading
core/java/android/text/AndroidBidi.java +2 −32 Original line number Original line Diff line number Diff line Loading @@ -17,11 +17,7 @@ package android.text; package android.text; import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage; import android.icu.lang.UCharacter; import android.icu.lang.UCharacterDirection; import android.icu.lang.UProperty; import android.icu.text.Bidi; import android.icu.text.Bidi; import android.icu.text.BidiClassifier; import android.text.Layout.Directions; import android.text.Layout.Directions; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; Loading @@ -33,32 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class AndroidBidi { public class AndroidBidi { /** * Overrides ICU {@link BidiClassifier} in order to correctly handle character directions for * newest emoji that ICU is not aware of. */ public static class EmojiBidiOverride extends BidiClassifier { public EmojiBidiOverride() { super(null /* No persisting object needed */); } // Tells ICU to use the standard Unicode value. private static final int NO_OVERRIDE = UCharacter.getIntPropertyMaxValue(UProperty.BIDI_CLASS) + 1; @Override public int classify(int c) { if (Emoji.isNewEmoji(c)) { // All new emoji characters in Unicode 10.0 are of the bidi class ON. return UCharacterDirection.OTHER_NEUTRAL; } else { return NO_OVERRIDE; } } } private static final EmojiBidiOverride sEmojiBidiOverride = new EmojiBidiOverride(); /** /** * Runs the bidi algorithm on input text. * Runs the bidi algorithm on input text. */ */ Loading @@ -82,7 +52,6 @@ public class AndroidBidi { default: paraLevel = Bidi.LTR; break; default: paraLevel = Bidi.LTR; break; } } final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */); final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */); icuBidi.setCustomClassifier(sEmojiBidiOverride); icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */); icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */); for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) { chInfo[i] = icuBidi.getLevelAt(i); chInfo[i] = icuBidi.getLevelAt(i); Loading Loading @@ -221,3 +190,4 @@ public class AndroidBidi { return new Directions(ld); return new Directions(ld); } } } }
core/java/android/text/BidiFormatter.java +2 −7 Original line number Original line Diff line number Diff line Loading @@ -604,16 +604,11 @@ public final class BidiFormatter { /** /** * Return Character directionality. Same as {@link Character#getDirectionality(int)} except * Return Character directionality. Same as {@link Character#getDirectionality(int)} except * it overrides values for newest emoji that are not covered by ICU. * it can override values for newest emoji that are not covered by ICU. */ */ public static byte getDirectionality(int codePoint) { public 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); return Character.getDirectionality(codePoint); } } } // Internal instance variables. // Internal instance variables. Loading
core/java/android/text/Emoji.java +3 −27 Original line number Original line Diff line number Diff line Loading @@ -60,40 +60,16 @@ public class Emoji { if (c == 0x1F91D || c == 0x1F93C) { if (c == 0x1F91D || c == 0x1F93C) { return true; return true; } } // Emoji Modifier Base characters new in Unicode emoji 11 // If Android's copy of ICU is behind, check for new codepoints here. // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt // Consult log for implementation pattern. // TODO: Remove once emoji-data.text 11 is in ICU or update to 11. if ((0x1F9B5 <= c && c <= 0x1F9B6) || (0x1F9B8 <= c && c <= 0x1F9B9)) { return true; } return UCharacter.hasBinaryProperty(c, UProperty.EMOJI_MODIFIER_BASE); return UCharacter.hasBinaryProperty(c, UProperty.EMOJI_MODIFIER_BASE); } } /** * Returns true if the character is a new emoji still not supported in our version of ICU. */ public static boolean isNewEmoji(int c) { // Emoji characters new in Unicode emoji 12 // From https://www.unicode.org/Public/emoji/12.0/emoji-data.txt // TODO: Remove once emoji-data.text 12 is in ICU or update to 12. if (c < 0x1F6D5 || c > 0x1FA95) { // Optimization for characters outside the new emoji range. return false; } return c == 0x1F6D5 || c == 0x1F6FA || c == 0x1F93F || c == 0x1F971 || c == 0x1F97B || (0x1F7E0 <= c && c <= 0x1F7EB) || (0x1F90D <= c && c <= 0x1F90F) || (0x1F9A5 <= c && c <= 0x1F9AA) || (0x1F9AE <= c && c <= 0x1F9AF) || (0x1F9BA <= c && c <= 0x1F9BF) || (0x1F9C3 <= c && c <= 0x1F9CA) || (0x1F9CD <= c && c <= 0x1F9CF) || (0x1FA70 <= c && c <= 0x1FA73) || (0x1FA78 <= c && c <= 0x1FA7A) || (0x1FA80 <= c && c <= 0x1FA82) || (0x1FA90 <= c && c <= 0x1FA95); } /** /** * Returns true if the character has Emoji property. * Returns true if the character has Emoji property. */ */ public static boolean isEmoji(int codePoint) { public static boolean isEmoji(int codePoint) { return isNewEmoji(codePoint) || UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI); return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI); } } // Returns true if the character can be a base character of COMBINING ENCLOSING KEYCAP. // Returns true if the character can be a base character of COMBINING ENCLOSING KEYCAP. Loading
core/tests/coretests/src/android/text/EmojiTest.java +26 −2 Original line number Original line Diff line number Diff line Loading @@ -83,12 +83,27 @@ public class EmojiTest { {0x1FA70, 0x1FA73}, {0x1FA70, 0x1FA73}, {0x1FA78, 0x1FA7A}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA82}, {0x1FA80, 0x1FA82}, {0x1FA90, 0x1FA95} {0x1FA90, 0x1FA95}, }, { // EMOJI 13 {0x1F6D6, 0x1F6D7}, {0x1F6FB, 0x1F6FC}, {0x1F90C, 0x1F90C}, {0x1F972, 0x1F972}, {0x1F977, 0x1F978}, {0x1F9A3, 0x1F9A4}, {0x1F9AB, 0x1F9AD}, {0x1F9CB, 0x1F9CB}, {0x1FA74, 0x1FA74}, {0x1FA83, 0x1FA86}, {0x1FA96, 0x1FAA8}, {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, } } }; }; final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */); final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */); icuBidi.setCustomClassifier(new AndroidBidi.EmojiBidiOverride()); for (int version = 0; version < data.length; version++) { for (int version = 0; version < data.length; version++) { for (int row = 0; row < data[version].length; row++) { for (int row = 0; row < data[version].length; row++) { Loading Loading @@ -125,6 +140,15 @@ public class EmojiTest { { // EMOJI 11 { // EMOJI 11 {0x1F9B5, 0x1F9B6}, {0x1F9B5, 0x1F9B6}, {0x1F9B8, 0x1F9B9} {0x1F9B8, 0x1F9B9} }, { // EMOJI 12 {0x1F90F, 0x1F90F}, {0x1F9BB, 0x1F9BB}, {0x1F9CD, 0x1F9CF}, }, { // EMOJI 13 {0x1F90C, 0x1F90C}, {0x1F977, 0x1F977} } } }; }; for (int version = 0; version < data.length; version++) { for (int version = 0; version < data.length; version++) { Loading