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

Commit 0d69c8dd authored by Holly Sun's avatar Holly Sun
Browse files

Add feature flag to control whether to inject fallback app results when AiAi...

Add feature flag to control whether to inject fallback app results when AiAi fails to return app corpus results.

Also add simple prefix matching method.

Bug: 255355348
Test: manual
Change-Id: I35f2089924d774f3bcc1c05638a5d6b45ed7443f
parent 8a03be18
Loading
Loading
Loading
Loading
+72 −49
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ import java.text.Collator;
public class StringMatcherUtility {

    /**
     * Returns {@code true} is {@code query} is a prefix substring of a complete word/phrase in
     * {@code target}.
     * Returns {@code true} if {@code query} is a prefix of a substring in {@code target}. How to
     * break target to valid substring is defined in the given {@code matcher}.
     */
    public static boolean matches(String query, String target, StringMatcher matcher) {
        int queryLength = query.length();
@@ -50,7 +50,7 @@ public class StringMatcherUtility {
            thisType = nextType;
            nextType = i < (targetLength - 1)
                    ? Character.getType(target.codePointAt(i + 1)) : Character.UNASSIGNED;
            if (isBreak(thisType, lastType, nextType)
            if (matcher.isBreak(thisType, lastType, nextType)
                    && matcher.matches(query, target.substring(i, i + queryLength))) {
                return true;
            }
@@ -59,14 +59,56 @@ public class StringMatcherUtility {
    }

    /**
     * Returns true if the current point should be a break point. Following cases
     * are considered as break points:
     * Performs locale sensitive string comparison using {@link Collator}.
     */
    public static class StringMatcher {

        private static final char MAX_UNICODE = '\uFFFF';

        private final Collator mCollator;

        StringMatcher() {
            // On android N and above, Collator uses ICU implementation which has a much better
            // support for non-latin locales.
            mCollator = Collator.getInstance();
            mCollator.setStrength(Collator.PRIMARY);
            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        }

        /**
         * Returns true if {@param query} is a prefix of {@param target}
         */
        public boolean matches(String query, String target) {
            switch (mCollator.compare(query, target)) {
                case 0:
                    return true;
                case -1:
                    // The target string can contain a modifier which would make it larger than
                    // the query string (even though the length is same). If the query becomes
                    // larger after appending a unicode character, it was originally a prefix of
                    // the target string and hence should match.
                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
                default:
                    return false;
            }
        }

        public static StringMatcher getInstance() {
            return new StringMatcher();
        }

        /**
         * Returns true if the current point should be a break point.
         *
         * Following cases are considered as break points:
         *     1) Any non space character after a space character
         *     2) Any digit after a non-digit character
         *     3) Any capital character after a digit or small character
         *     4) Any capital character before a small character
         *
         * E.g., "YouTube" matches the input "you" and "tube", but not "out".
         */
    private static boolean isBreak(int thisType, int prevType, int nextType) {
        protected boolean isBreak(int thisType, int prevType, int nextType) {
            switch (prevType) {
                case Character.UNASSIGNED:
                case Character.SPACE_SEPARATOR:
@@ -103,44 +145,25 @@ public class StringMatcherUtility {
                    return  false;
            }
        }
    }

    /**
     * Performs locale sensitive string comparison using {@link Collator}.
     * Subclass of {@code StringMatcher} using simple space break for prefix matching.
     * E.g., "YouTube" matches the input "you". "Play Store" matches the input "play".
     */
    public static class StringMatcher {

        private static final char MAX_UNICODE = '\uFFFF';

        private final Collator mCollator;
    public static class StringMatcherSpace extends StringMatcher {

        StringMatcher() {
            // On android N and above, Collator uses ICU implementation which has a much better
            // support for non-latin locales.
            mCollator = Collator.getInstance();
            mCollator.setStrength(Collator.PRIMARY);
            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        public static StringMatcherSpace getInstance() {
            return new StringMatcherSpace();
        }

        /**
         * Returns true if {@param query} is a prefix of {@param target}
         * The first character or any character after a space is considered as a break point.
         * Returns true if the current point should be a break point.
         */
        public boolean matches(String query, String target) {
            switch (mCollator.compare(query, target)) {
                case 0:
                    return true;
                case -1:
                    // The target string can contain a modifier which would make it larger than
                    // the query string (even though the length is same). If the query becomes
                    // larger after appending a unicode character, it was originally a prefix of
                    // the target string and hence should match.
                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
                default:
                    return false;
            }
        }

        public static StringMatcher getInstance() {
            return new StringMatcher();
        @Override
        protected boolean isBreak(int thisType, int prevType, int nextType) {
            return prevType == Character.UNASSIGNED || prevType == Character.SPACE_SEPARATOR;
        }
    }

+47 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
import com.android.launcher3.search.StringMatcherUtility.StringMatcherSpace;

import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,11 +35,12 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StringMatcherUtilityTest {
    private static final StringMatcher MATCHER =
            StringMatcher.getInstance();
    private static final StringMatcher MATCHER = StringMatcher.getInstance();
    private static final StringMatcherSpace MATCHER_SPACE = StringMatcherSpace.getInstance();

    @Test
    public void testMatches() {
        assertTrue(matches("white", "white cow", MATCHER));
        assertTrue(matches("white ", "white cow", MATCHER));
        assertTrue(matches("white c", "white cow", MATCHER));
        assertTrue(matches("cow", "white cow", MATCHER));
@@ -93,4 +95,47 @@ public class StringMatcherUtilityTest {
        assertFalse(matches("ㄷ", "로드라이브", MATCHER));
        assertFalse(matches("åç", "abc", MATCHER));
    }

    @Test
    public void testMatchesWithSpaceBreakOnly() {
        assertTrue(matches("white", "white cow", MATCHER_SPACE));
        assertTrue(matches("white ", "white cow", MATCHER_SPACE));
        assertTrue(matches("white c", "white cow", MATCHER_SPACE));
        assertTrue(matches("cow", "white cow", MATCHER_SPACE));
        assertTrue(matches("cow", "whitecow cow", MATCHER_SPACE));

        assertFalse(matches("cow", "whiteCow", MATCHER_SPACE));
        assertFalse(matches("cow", "whiteCOW", MATCHER_SPACE));
        assertFalse(matches("cow", "whitecowCOW", MATCHER_SPACE));
        assertFalse(matches("cow", "white2cow", MATCHER_SPACE));
        assertFalse(matches("cow", "whitecow", MATCHER_SPACE));
        assertFalse(matches("cow", "whitEcow", MATCHER_SPACE));
        assertFalse(matches("cow", "whitecowCow", MATCHER_SPACE));
        assertFalse(matches("cow", "whitecowcow", MATCHER_SPACE));
        assertFalse(matches("cow", "whit ecowcow", MATCHER_SPACE));

        assertFalse(matches("dog", "cats&dogs", MATCHER_SPACE));
        assertFalse(matches("dog", "cats&Dogs", MATCHER_SPACE));
        assertFalse(matches("&", "cats&Dogs", MATCHER_SPACE));

        assertFalse(matches("43", "2+43", MATCHER_SPACE));
        assertFalse(matches("3", "2+43", MATCHER_SPACE));

        assertTrue(matches("q", "Q", MATCHER_SPACE));
        assertTrue(matches("q", "  Q", MATCHER_SPACE));

        // match lower case words
        assertTrue(matches("e", "elephant", MATCHER_SPACE));
        assertTrue(matches("eL", "Elephant", MATCHER_SPACE));

        assertTrue(matches("电", "电子邮件", MATCHER_SPACE));
        assertTrue(matches("电子", "电子邮件", MATCHER_SPACE));
        assertTrue(matches("子", "电子邮件", MATCHER_SPACE));
        assertTrue(matches("邮件", "电子邮件", MATCHER_SPACE));

        assertFalse(matches("ba", "Bot", MATCHER_SPACE));
        assertFalse(matches("ba", "bot", MATCHER_SPACE));
        assertFalse(matches("phant", "elephant", MATCHER_SPACE));
        assertFalse(matches("elephants", "elephant", MATCHER_SPACE));
    }
}