summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Siyamed Sinir <siyamed@google.com> 2018-04-12 17:29:56 -0700
committer Siyamed Sinir <siyamed@google.com> 2018-04-13 14:00:15 -0700
commit213388d2548360b2726493ffc583e5d78a8b012f (patch)
tree6c48ab8bdb6add4d66e2e625e3f9146e410413ed
parentb5f88e7e300bc6b60f3313024af3c4ec958e44ac (diff)
Update utility class Emoji.java for Emoji 11
This CL removes the Emoji 5.0 info from Emoji.java since they are supported by ICU. It adds emoji added in Emoji 11. Test: atest android.text.EmojiTest Test: Verified that ICU handles Emoji 5.0 emoji Test: Verified hardware keyboard backspace with hair color emoji Test: Visually verified cursor moves with hardware keyboard Bug: 77148691 Change-Id: I40b290fcea201cf5e35ad4c461f8d8056b8c3739
-rw-r--r--core/java/android/text/AndroidBidi.java8
-rw-r--r--core/java/android/text/BidiFormatter.java12
-rw-r--r--core/java/android/text/Emoji.java51
-rw-r--r--core/tests/coretests/src/android/text/EmojiTest.java120
4 files changed, 164 insertions, 27 deletions
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index 179d545f8ccd..72383cf377e9 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -32,8 +32,12 @@ import com.android.internal.annotations.VisibleForTesting;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class AndroidBidi {
- private static class EmojiBidiOverride extends BidiClassifier {
- EmojiBidiOverride() {
+ /**
+ * 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 */);
}
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index f65f39762b75..77f17a7d2377 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -21,6 +21,8 @@ import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
import android.annotation.Nullable;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Locale;
/**
@@ -570,8 +572,10 @@ public final class BidiFormatter {
/**
* An object that estimates the directionality of a given string by various methods.
*
+ * @hide
*/
- private static class DirectionalityEstimator {
+ @VisibleForTesting
+ public static class DirectionalityEstimator {
// Internal static variables and constants.
@@ -598,7 +602,11 @@ public final class BidiFormatter {
}
}
- private static byte getDirectionality(int codePoint) {
+ /**
+ * Return Character directionality. Same as {@link Character#getDirectionality(int)} except
+ * it overrides values for newest emoji that are not covered by ICU.
+ */
+ 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;
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index d33aad99e1a5..876c64eebf84 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -46,44 +46,49 @@ public class Emoji {
return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER);
}
- // Returns true if the given code point is emoji modifier base.
- public static boolean isEmojiModifierBase(int codePoint) {
+ //
+
+ /**
+ * Returns true if the given code point is emoji modifier base.
+ * @param c codepoint to check
+ * @return true if is emoji modifier base
+ */
+ public static boolean isEmojiModifierBase(int c) {
// These two characters were removed from Emoji_Modifier_Base in Emoji 4.0, but we need to
// keep them as emoji modifier bases since there are fonts and user-generated text out there
// that treats these as potential emoji bases.
- if (codePoint == 0x1F91D || codePoint == 0x1F93C) {
+ if (c == 0x1F91D || c == 0x1F93C) {
return true;
}
- // Emoji Modifier Base 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 (codePoint == 0x1F91F
- || (0x1F931 <= codePoint && codePoint <= 0x1F932)
- || (0x1F9D1 <= codePoint && codePoint <= 0x1F9DD)) {
+ // Emoji Modifier Base characters new in Unicode emoji 11
+ // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
+ // 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(codePoint, 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 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 (codePoint < 0x1F6F7 || codePoint > 0x1F9E6) {
+ public static boolean isNewEmoji(int c) {
+ // Emoji characters new in Unicode emoji 11
+ // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
+ // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
+ if (c < 0x1F6F9 || c > 0x1F9FF) {
// 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 c == 0x265F || c == 0x267E || c == 0x1F6F9 || c == 0x1F97A
+ || (0x1F94D <= c && c <= 0x1F94F)
+ || (0x1F96C <= c && c <= 0x1F970)
+ || (0x1F973 <= c && c <= 0x1F976)
+ || (0x1F97C <= c && c <= 0x1F97F)
+ || (0x1F998 <= c && c <= 0x1F9A2)
+ || (0x1F9B0 <= c && c <= 0x1F9B9)
+ || (0x1F9C1 <= c && c <= 0x1F9C2)
+ || (0x1F9E7 <= c && c <= 0x1F9FF);
}
/**
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
new file mode 100644
index 000000000000..313f1b6a6614
--- /dev/null
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.icu.lang.UCharacterDirection;
+import android.icu.text.Bidi;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Emoji and ICU drops does not happen at the same time. Therefore there are almost always cases
+ * where the existing ICU version is not aware of the latest emoji that Android supports.
+ * This test covers Emoji and ICU related functions where other components such as
+ * {@link AndroidBidi}, {@link BidiFormatter} depend on. The tests are collected into the same
+ * class since the changes effect all those classes.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiTest {
+
+ @Test
+ public void testIsNewEmoji_Emoji5() {
+ // each row in the data is the range of emoji
+ final int[][][] data = new int[][][]{
+ { // EMOJI 5
+ // range of emoji: i.e from 0x1F6F7 to 0x1F6F8 inclusive
+ {0x1F6F7, 0x1F6F8},
+ {0x1F91F, 0x1F91F},
+ {0x1F928, 0x1F92F},
+ {0x1F94C, 0x1F94C},
+ {0x1F95F, 0x1F96B},
+ {0x1F992, 0x1F997},
+ {0x1F9D0, 0x1F9E6},
+ },
+ { // EMOJI 11
+ {0x265F, 0x265F},
+ {0x267E, 0x267E},
+ {0x1F6F9, 0x1F6F9},
+ {0x1F94D, 0x1F94F},
+ {0x1F96C, 0x1F970},
+ {0x1F973, 0x1F976},
+ {0x1F97A, 0x1F97A},
+ {0x1F97C, 0x1F97F},
+ {0x1F998, 0x1F9A2},
+ {0x1F9B0, 0x1F9B9},
+ {0x1F9C1, 0x1F9C2},
+ {0x1F9E7, 0x1F9FF},
+ }
+ };
+
+ final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */);
+ icuBidi.setCustomClassifier(new AndroidBidi.EmojiBidiOverride());
+
+ for (int version = 0; version < data.length; version++) {
+ for (int row = 0; row < data[version].length; row++) {
+ for (int c = data[version][row][0]; c < data[version][row][1]; c++) {
+ assertTrue(Integer.toHexString(c) + " should be emoji", Emoji.isEmoji(c));
+
+ assertEquals(Integer.toHexString(c) + " should have neutral directionality",
+ Character.DIRECTIONALITY_OTHER_NEUTRALS,
+ BidiFormatter.DirectionalityEstimator.getDirectionality(c));
+
+ assertEquals(Integer.toHexString(c) + " shoud be OTHER_NEUTRAL for ICU Bidi",
+ UCharacterDirection.OTHER_NEUTRAL, icuBidi.getCustomizedClass(c));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testisEmojiModifierBase_LegacyCompat() {
+ assertTrue(Emoji.isEmojiModifierBase(0x1F91D));
+ assertTrue(Emoji.isEmojiModifierBase(0x1F93C));
+ }
+
+ @Test
+ public void testisEmojiModifierBase() {
+ // each row in the data is the range of emoji
+ final int[][][] data = new int[][][]{
+ { // EMOJI 5
+ // range of emoji: i.e from 0x1F91F to 0x1F91F inclusive
+ {0x1F91F, 0x1F91F},
+ {0x1F931, 0x1F932},
+ {0x1F9D1, 0x1F9DD},
+ },
+ { // EMOJI 11
+ {0x1F9B5, 0x1F9B6},
+ {0x1F9B8, 0x1F9B9}
+ }
+ };
+ for (int version = 0; version < data.length; version++) {
+ for (int row = 0; row < data[version].length; row++) {
+ for (int c = data[version][row][0]; c < data[version][row][1]; c++) {
+ assertTrue(Integer.toHexString(c) + " should be emoji modifier base",
+ Emoji.isEmojiModifierBase(c));
+ }
+ }
+ }
+ }
+}