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
This commit is contained in:
@@ -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 */);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
120
core/tests/coretests/src/android/text/EmojiTest.java
Normal file
120
core/tests/coretests/src/android/text/EmojiTest.java
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user