From 0d69c8dde936f9fbb72e7fd835dc78c119b220ec Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Wed, 19 Oct 2022 17:40:52 -0700 Subject: [PATCH] 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 --- .../search/StringMatcherUtility.java | 121 +++++++++++------- .../search/StringMatcherUtilityTest.java | 49 ++++++- 2 files changed, 119 insertions(+), 51 deletions(-) diff --git a/src/com/android/launcher3/search/StringMatcherUtility.java b/src/com/android/launcher3/search/StringMatcherUtility.java index acab52bbe8..c66f3a19e4 100644 --- a/src/com/android/launcher3/search/StringMatcherUtility.java +++ b/src/com/android/launcher3/search/StringMatcherUtility.java @@ -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; } @@ -58,52 +58,6 @@ public class StringMatcherUtility { return false; } - /** - * 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 - */ - private static boolean isBreak(int thisType, int prevType, int nextType) { - switch (prevType) { - case Character.UNASSIGNED: - case Character.SPACE_SEPARATOR: - case Character.LINE_SEPARATOR: - case Character.PARAGRAPH_SEPARATOR: - return true; - } - switch (thisType) { - case Character.UPPERCASE_LETTER: - if (nextType == Character.UPPERCASE_LETTER) { - return true; - } - // Follow through - case Character.TITLECASE_LETTER: - // Break point if previous was not a upper case - return prevType != Character.UPPERCASE_LETTER; - case Character.LOWERCASE_LETTER: - // Break point if previous was not a letter. - return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED; - case Character.DECIMAL_DIGIT_NUMBER: - case Character.LETTER_NUMBER: - case Character.OTHER_NUMBER: - // Break point if previous was not a number - return !(prevType == Character.DECIMAL_DIGIT_NUMBER - || prevType == Character.LETTER_NUMBER - || prevType == Character.OTHER_NUMBER); - case Character.MATH_SYMBOL: - case Character.CURRENCY_SYMBOL: - case Character.OTHER_PUNCTUATION: - case Character.DASH_PUNCTUATION: - // Always a break point for a symbol - return true; - default: - return false; - } - } - /** * Performs locale sensitive string comparison using {@link Collator}. */ @@ -142,6 +96,75 @@ public class StringMatcherUtility { 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". + */ + protected boolean isBreak(int thisType, int prevType, int nextType) { + switch (prevType) { + case Character.UNASSIGNED: + case Character.SPACE_SEPARATOR: + case Character.LINE_SEPARATOR: + case Character.PARAGRAPH_SEPARATOR: + return true; + } + switch (thisType) { + case Character.UPPERCASE_LETTER: + if (nextType == Character.UPPERCASE_LETTER) { + return true; + } + // Follow through + case Character.TITLECASE_LETTER: + // Break point if previous was not a upper case + return prevType != Character.UPPERCASE_LETTER; + case Character.LOWERCASE_LETTER: + // Break point if previous was not a letter. + return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED; + case Character.DECIMAL_DIGIT_NUMBER: + case Character.LETTER_NUMBER: + case Character.OTHER_NUMBER: + // Break point if previous was not a number + return !(prevType == Character.DECIMAL_DIGIT_NUMBER + || prevType == Character.LETTER_NUMBER + || prevType == Character.OTHER_NUMBER); + case Character.MATH_SYMBOL: + case Character.CURRENCY_SYMBOL: + case Character.OTHER_PUNCTUATION: + case Character.DASH_PUNCTUATION: + // Always a break point for a symbol + return true; + default: + return false; + } + } + } + + /** + * 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 StringMatcherSpace extends StringMatcher { + + public static StringMatcherSpace getInstance() { + return new StringMatcherSpace(); + } + + /** + * 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. + */ + @Override + protected boolean isBreak(int thisType, int prevType, int nextType) { + return prevType == Character.UNASSIGNED || prevType == Character.SPACE_SEPARATOR; + } } /** diff --git a/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java index 413f404406..3b53255550 100644 --- a/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java +++ b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java @@ -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)); + } }