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

Commit 213388d2 authored by Siyamed Sinir's avatar Siyamed Sinir
Browse files

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
parent b5f88e7e
Loading
Loading
Loading
Loading
+6 −2
Original line number Original line Diff line number Diff line
@@ -32,8 +32,12 @@ import com.android.internal.annotations.VisibleForTesting;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class AndroidBidi {
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 */);
            super(null /* No persisting object needed */);
        }
        }


+10 −2
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.text.TextDirectionHeuristics.FIRSTSTRONG_LTR;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.view.View;
import android.view.View;


import com.android.internal.annotations.VisibleForTesting;

import java.util.Locale;
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.
     * 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.
        // 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)) {
            if (Emoji.isNewEmoji(codePoint)) {
                // TODO: Fix or remove once emoji-data.text 5.0 is in ICU or update to 6.0.
                // TODO: Fix or remove once emoji-data.text 5.0 is in ICU or update to 6.0.
                return Character.DIRECTIONALITY_OTHER_NEUTRALS;
                return Character.DIRECTIONALITY_OTHER_NEUTRALS;
+28 −23
Original line number Original line Diff line number Diff line
@@ -46,44 +46,49 @@ public class Emoji {
        return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER);
        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
        // 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
        // keep them as emoji modifier bases since there are fonts and user-generated text out there
        // that treats these as potential emoji bases.
        // that treats these as potential emoji bases.
        if (codePoint == 0x1F91D || codePoint == 0x1F93C) {
        if (c == 0x1F91D || c == 0x1F93C) {
            return true;
            return true;
        }
        }
        // Emoji Modifier Base characters new in Unicode emoji 5.0.
        // Emoji Modifier Base characters new in Unicode emoji 11
        // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
        // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
        // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
        // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
        if (codePoint == 0x1F91F
        if ((0x1F9B5 <= c && c <= 0x1F9B6) || (0x1F9B8 <= c && c <= 0x1F9B9)) {
                || (0x1F931 <= codePoint && codePoint <= 0x1F932)
                || (0x1F9D1 <= codePoint && codePoint <= 0x1F9DD)) {
            return true;
            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.
     * Returns true if the character is a new emoji still not supported in our version of ICU.
     */
     */
    public static boolean isNewEmoji(int codePoint) {
    public static boolean isNewEmoji(int c) {
        // Emoji characters new in Unicode emoji 5.0.
        // Emoji characters new in Unicode emoji 11
        // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
        // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
        // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
        // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
        if (codePoint < 0x1F6F7 || codePoint > 0x1F9E6) {
        if (c < 0x1F6F9 || c > 0x1F9FF) {
            // Optimization for characters outside the new emoji range.
            // Optimization for characters outside the new emoji range.
            return false;
            return false;
        }
        }
        return (0x1F6F7 <= codePoint && codePoint <= 0x1F6F8)
        return c == 0x265F || c == 0x267E || c == 0x1F6F9 || c == 0x1F97A
                || codePoint == 0x1F91F
                || (0x1F94D <= c && c <= 0x1F94F)
                || (0x1F928 <= codePoint && codePoint <= 0x1F92F)
                || (0x1F96C <= c && c <= 0x1F970)
                || (0x1F931 <= codePoint && codePoint <= 0x1F932)
                || (0x1F973 <= c && c <= 0x1F976)
                || codePoint == 0x1F94C
                || (0x1F97C <= c && c <= 0x1F97F)
                || (0x1F95F <= codePoint && codePoint <= 0x1F96B)
                || (0x1F998 <= c && c <= 0x1F9A2)
                || (0x1F992 <= codePoint && codePoint <= 0x1F997)
                || (0x1F9B0 <= c && c <= 0x1F9B9)
                || (0x1F9D0 <= codePoint && codePoint <= 0x1F9E6);
                || (0x1F9C1 <= c && c <= 0x1F9C2)
                || (0x1F9E7 <= c && c <= 0x1F9FF);
    }
    }


    /**
    /**
+120 −0
Original line number Original line 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));
                }
            }
        }
    }
}