Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adcb6cac authored by Siyamed Sinir's avatar Siyamed Sinir
Browse files

Update 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
(cherry picked from commit 213388d2)
parent 08a20282
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -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 */);
        }

+10 −2
Original line number Diff line number Diff line
@@ -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;
+28 −23
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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));
                }
            }
        }
    }
}