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

Commit de3299f0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[PM] Don't return the trimmed label" into main

parents 5675ae13 719b2fdb
Loading
Loading
Loading
Loading
+61 −127
Original line number Diff line number Diff line
@@ -43,9 +43,11 @@ public class LauncherActivityInfo {
    private final PackageManager mPm;
    private final LauncherActivityInfoInternal mInternal;

    private static final UnicodeSet TRIMMABLE_CHARACTERS =
    private static final UnicodeSet INVISIBLE_CHARACTERS =
            new UnicodeSet("[[:White_Space:][:Default_Ignorable_Code_Point:][:gc=Cc:]]",
                    /* ignoreWhitespace= */ false).freeze();
    // Only allow 3 consecutive invisible characters in the prefix of the string.
    private static final int PREFIX_CONSECUTIVE_INVISIBLE_CHARACTERS_MAXIMUM = 3;

    /**
     * Create a launchable activity object for a given ResolveInfo and user.
@@ -93,19 +95,23 @@ public class LauncherActivityInfo {
            return getActivityInfo().loadLabel(mPm);
        }

        CharSequence label = trim(getActivityInfo().loadLabel(mPm));
        // If the trimmed label is empty, use application's label instead
        if (TextUtils.isEmpty(label)) {
            label = trim(getApplicationInfo().loadLabel(mPm));
            // If the trimmed label is still empty, use package name instead
            if (TextUtils.isEmpty(label)) {
                label = getComponentName().getPackageName();
            }
        CharSequence label = getActivityInfo().loadLabel(mPm).toString().trim();
        // If the activity label is visible to the user, return the original activity label
        if (isVisible(label)) {
            return label;
        }
        // TODO: Go through LauncherAppsService

        // Use application label instead
        label = getApplicationInfo().loadLabel(mPm).toString().trim();
        // If the application label is visible to the user, return the original application label
        if (isVisible(label)) {
            return label;
        }

        // Use package name instead
        return getComponentName().getPackageName();
    }

    /**
     * @return Package loading progress, range between [0, 1].
     */
@@ -207,147 +213,75 @@ public class LauncherActivityInfo {
    }

    /**
     * If the {@code ch} is trimmable, return {@code true}. Otherwise, return
     * {@code false}. If the count of the code points of {@code ch} doesn't
     * equal 1, return {@code false}.
     * Check whether the {@code sequence} is visible to the user or not.
     * <p>
     * There are two types of the trimmable characters.
     * 1. The character is one of the Default_Ignorable_Code_Point in
     * Return {@code false} when one of these conditions are satisfied:
     * 1. The {@code sequence} starts with at least consecutive three invisible characters.
     * 2. The sequence is composed of the invisible characters and non-glyph characters.
     * <p>
     * Invisible character is one of the Default_Ignorable_Code_Point in
     * <a href="
     * https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt">
     * DerivedCoreProperties.txt</a>, the White_Space in <a href=
     * "https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt">PropList.txt
     * </a> or category Cc.
     * <p>
     * 2. The character is not supported in the current system font.
     * Non-glyph character means the character is not supported in the current system font.
     * {@link android.graphics.Paint#hasGlyph(String)}
     * <p>
     *
     */
    private static boolean isTrimmable(@NonNull Paint paint, @NonNull CharSequence ch) {
        Objects.requireNonNull(paint);
        Objects.requireNonNull(ch);

        // if ch is empty or it is not a character (i,e, the count of code
        // point doesn't equal one), return false
        if (TextUtils.isEmpty(ch)
                || Character.codePointCount(ch, /* beginIndex= */ 0, ch.length()) != 1) {
            return false;
        }

        // Return true for the cases as below:
        // 1. The character is in the TRIMMABLE_CHARACTERS set
        // 2. The character is not supported in the system font
        return TRIMMABLE_CHARACTERS.contains(ch) || !paint.hasGlyph(ch.toString());
    }

    /**
     * If the {@code sequence} has some leading trimmable characters, creates a new copy
     * and removes the trimmable characters from the copy. Otherwise the given
     * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
     * to determine whether the character is trimmable or not.
     *
     * @return the trimmed string or the original string that has no
     *         leading trimmable characters.
     * @see    #isTrimmable(Paint, CharSequence)
     * @see    #trim(CharSequence)
     * @see    #trimEnd(CharSequence)
     *
     * @hide
     */
    @VisibleForTesting
    @NonNull
    public static CharSequence trimStart(@NonNull CharSequence sequence) {
    public static boolean isVisible(@NonNull CharSequence sequence) {
        Objects.requireNonNull(sequence);

        if (TextUtils.isEmpty(sequence)) {
            return sequence;
            return false;
        }

        final Paint paint = new Paint();
        int trimCount = 0;
        int invisibleCharCount = 0;
        int notSupportedCharCount = 0;
        final int[] codePoints = sequence.codePoints().toArray();
        for (int i = 0, length = codePoints.length; i < length; i++) {
            String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
            if (!isTrimmable(paint, ch)) {
                break;
            }
            trimCount += ch.length();
        }
        if (trimCount == 0) {
            return sequence;
        }
        return sequence.subSequence(trimCount, sequence.length());
    }

    /**
     * If the {@code sequence} has some trailing trimmable characters, creates a new copy
     * and removes the trimmable characters from the copy. Otherwise the given
     * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
     * to determine whether the character is trimmable or not.
     *
     * @return the trimmed sequence or the original sequence that has no
     *         trailing trimmable characters.
     * @see    #isTrimmable(Paint, CharSequence)
     * @see    #trimStart(CharSequence)
     * @see    #trim(CharSequence)
     *
     * @hide
     */
    @VisibleForTesting
    @NonNull
    public static CharSequence trimEnd(@NonNull CharSequence sequence) {
        Objects.requireNonNull(sequence);

        if (TextUtils.isEmpty(sequence)) {
            return sequence;
        }

        final Paint paint = new Paint();
        int trimCount = 0;
        final int[] codePoints = sequence.codePoints().toArray();
        for (int i = codePoints.length - 1; i >= 0; i--) {
            String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
            if (!isTrimmable(paint, ch)) {
                break;
            }
            trimCount += ch.length();
        }

        if (trimCount == 0) {
            return sequence;
            // The check steps:
            // 1. If the character is contained in INVISIBLE_CHARACTERS, invisibleCharCount++.
            //    1.1 Check whether the invisibleCharCount is larger or equal to
            //        PREFIX_INVISIBLE_CHARACTERS_MAXIMUM when notSupportedCharCount is zero.
            //        It means that there are three consecutive invisible characters at the
            //        start of the string, return false.
            //    Otherwise, continue.
            // 2. If the character is not supported on the system:
            //    notSupportedCharCount++, continue
            // 3. If it does not continue or return on the above two cases, it means the
            //    character is visible and supported on the system, break.
            // After going through the whole string, if the sum of invisibleCharCount
            // and notSupportedCharCount is smaller than the length of the string, it
            // means the string has the other visible characters, return true.
            // Otherwise, return false.
            if (INVISIBLE_CHARACTERS.contains(ch)) {
                invisibleCharCount++;
                // If there are three successive invisible characters at the start of the
                // string, it is hard to visible to the user.
                if (notSupportedCharCount == 0
                        && invisibleCharCount >= PREFIX_CONSECUTIVE_INVISIBLE_CHARACTERS_MAXIMUM) {
                    return false;
                }
        return sequence.subSequence(0, sequence.length() - trimCount);
                continue;
            }

    /**
     * If the {@code sequence} has some leading or trailing trimmable characters, creates
     * a new copy and removes the trimmable characters from the copy. Otherwise the given
     * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
     * to determine whether the character is trimmable or not.
     *
     * @return the trimmed sequence or the original sequence that has no leading or
     *         trailing trimmable characters.
     * @see    #isTrimmable(Paint, CharSequence)
     * @see    #trimStart(CharSequence)
     * @see    #trimEnd(CharSequence)
     *
     * @hide
     */
    @VisibleForTesting
    @NonNull
    public static CharSequence trim(@NonNull CharSequence sequence) {
        Objects.requireNonNull(sequence);

        if (TextUtils.isEmpty(sequence)) {
            return sequence;
            // The character is not supported on the system, but it may not be an invisible
            // character. E.g. tofu (a rectangle).
            if (!paint.hasGlyph(ch)) {
                notSupportedCharCount++;
                continue;
            }

        CharSequence result = trimStart(sequence);
        if (TextUtils.isEmpty(result)) {
            return result;
            // The character is visible and supported on the system, break the for loop
            break;
        }

        return trimEnd(result);
        return (invisibleCharCount + notSupportedCharCount < codePoints.length);
    }
}
+158 −60
Original line number Diff line number Diff line
@@ -33,71 +33,169 @@ import org.junit.runner.RunWith;
public class LauncherActivityInfoTest {

    @Test
    public void testTrimStart() {
        // Invisible case
        assertThat(LauncherActivityInfo.trimStart("\u0009").toString()).isEmpty();
        // It is not supported in the system font
        assertThat(LauncherActivityInfo.trimStart("\u0FE1").toString()).isEmpty();
        // Surrogates case
        assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36").toString())
                .isEqualTo("\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trimStart("\u0009\u0FE1\uD83E\uDD36A").toString())
                .isEqualTo("\uD83E\uDD36A");
        assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
        assertThat(LauncherActivityInfo.trimStart("A\uD83E\uDD36\u0009\u0FE1A").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
        assertThat(LauncherActivityInfo.trimStart(
                "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trimStart(
                "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
    public void testIsVisible_normal() {
        // normal
        assertThat(LauncherActivityInfo.isVisible("label")).isTrue();
        // 1 surrogates case
        assertThat(LauncherActivityInfo.isVisible("\uD83E\uDD36")).isTrue();
    }

    @Test
    public void testTrimEnd() {
        // Invisible case
        assertThat(LauncherActivityInfo.trimEnd("\u0009").toString()).isEmpty();
        // It is not supported in the system font
        assertThat(LauncherActivityInfo.trimEnd("\u0FE1").toString()).isEmpty();
        // Surrogates case
        assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36").toString())
                .isEqualTo("\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trimEnd("\u0009\u0FE1\uD83E\uDD36A").toString())
                .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
        assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\uD83E\uDD36A");
        assertThat(LauncherActivityInfo.trimEnd("A\uD83E\uDD36\u0009\u0FE1A").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
        assertThat(LauncherActivityInfo.trimEnd(
                "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trimEnd(
                "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
    public void testIsVisible_onlyInvisibleCharacter() {
        // 1 invisible
        assertThat(LauncherActivityInfo.isVisible("\u0009")).isFalse();
        // 2 invisible
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164")).isFalse();
        // 3 invisible
        assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164")).isFalse();
        // 4 invisible
        assertThat(LauncherActivityInfo.isVisible("\u200F\u3000\u0009\u3164")).isFalse();
    }

    @Test
    public void testTrim() {
        // Invisible case
        assertThat(LauncherActivityInfo.trim("\u0009").toString()).isEmpty();
        // It is not supported in the system font
        assertThat(LauncherActivityInfo.trim("\u0FE1").toString()).isEmpty();
        // Surrogates case
        assertThat(LauncherActivityInfo.trim("\uD83E\uDD36").toString())
                .isEqualTo("\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trim("\u0009\u0FE1\uD83E\uDD36A").toString())
                .isEqualTo("\uD83E\uDD36A");
        assertThat(LauncherActivityInfo.trim("\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\uD83E\uDD36A");
        assertThat(LauncherActivityInfo.trim("A\uD83E\uDD36\u0009\u0FE1A").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
        assertThat(LauncherActivityInfo.trim(
                "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
                .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
        assertThat(LauncherActivityInfo.trim(
                "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
                .isEqualTo("\uD83E\uDD36A");
    public void testIsVisible_onlyNotSupportedCharacter() {
        // 1 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0FE1")).isFalse();
        // 2 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2")).isFalse();
        // 3 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3")).isFalse();
        // 4 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();
    }

    @Test
    public void testIsVisible_invisibleAndNotSupportedCharacter() {
        // 1 invisible, 1 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1")).isFalse();
        // 1 invisible, 2 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2")).isFalse();
        // 1 invisible, 3 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2\u0FE3")).isFalse();
        // 1 invisible, 4 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();

        // 2 invisible, 1 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1")).isFalse();
        // 2 invisible, 2 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\u0FE2")).isFalse();
        // 2 invisible, 3 not supported
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
        // 2 invisible, 4 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();

        // 3 invisible, 1 not supported
        assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\u0FE1")).isFalse();
        // 3 invisible, 2 not supported
        assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\u0FE1\u0FE2")).isFalse();
        // 3 invisible, 3 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
        // 3 invisible, 4 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();

        // 4 invisible, 1 not supported
        assertThat(LauncherActivityInfo.isVisible("\u200F\u3000\u0009\u3164\u0FE1")).isFalse();
        // 4 invisible, 2 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2")).isFalse();
        // 4 invisible, 3 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3")).isFalse();
        // 4 invisible, 4 not supported
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4")).isFalse();

        // 1 not supported, 1 invisible,
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009")).isFalse();
        // 1 not supported, 2 invisible
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\u3164")).isFalse();
        // 1 not supported, 3 invisible
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u3000\u0009\u3164")).isFalse();
        // 1 not supported, 4 invisible
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u200F\u3000\u0009\u3164")).isFalse();
    }

    @Test
    public void testIsVisible_invisibleAndNormalCharacter() {
        // 1 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0009\uD83E\uDD36")).isTrue();
        // 2 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\uD83E\uDD36")).isTrue();
        // 3 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u3000\u0009\u3164\uD83E\uDD36")).isFalse();
        // 4 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\uD83E\uDD36")).isFalse();
    }

    @Test
    public void testIsVisible_notSupportedAndNormalCharacter() {
        // 1 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\uD83E\uDD36")).isTrue();
        // 2 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\uD83E\uDD36")).isTrue();
        // 3 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isTrue();
        // 4 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0FE1\u0FE2\u0FE3\u0FE4\uD83E\uDD36")).isTrue();
    }

    @Test
    public void testIsVisible_mixAllCharacter() {
        // 1 invisible, 1 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\uD83E\uDD36")).isTrue();
        // 1 invisible, 1 not supported, 1 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0009\u0FE1\u3164\uD83E\uDD36")).isTrue();
        // 1 invisible, 1 not supported, 2 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0009\u0FE1\u3000\u3164\uD83E\uDD36")).isTrue();
        // 1 invisible, 1 not supported, 3 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0009\u0FE1\u200F\u3000\u3164\uD83E\uDD36")).isTrue();

        // 2 invisible, 1 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0009\u3164\u0FE1\uD83E\uDD36")).isTrue();
        // 2 invisible, 2 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isTrue();

        // 3 invisible, 1 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u3000\u0009\u3164\u0FE1\uD83E\uDD36")).isFalse();
        // 3 invisible, 2 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u3000\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isFalse();
        // 3 invisible, 3 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isFalse();

        // 4 invisible, 1 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\uD83E\uDD36")).isFalse();
        // 4 invisible, 2 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\uD83E\uDD36")).isFalse();
        // 4 invisible, 3 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\uD83E\uDD36")).isFalse();
        // 4 invisible, 4 not supported, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u200F\u3000\u0009\u3164\u0FE1\u0FE2\u0FE3\u0FE4\uD83E\uDD36")).isFalse();

        // 1 not supported, 1 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\uD83E\uDD36")).isTrue();
        // 1 not supported, 2 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible("\u0FE1\u0009\u3164\uD83E\uDD36")).isTrue();
        // 1 not supported, 3 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0FE1\u3000\u0009\u3164\uD83E\uDD36")).isTrue();
        // 1 not supported, 4 invisible, 1 surrogates
        assertThat(LauncherActivityInfo.isVisible(
                "\u0FE1\u200F\u3000\u0009\u3164\uD83E\uDD36")).isTrue();

    }
}