Loading src/com/android/settings/search2/InstalledAppResultLoader.java +46 −21 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import java.util.List; public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { private static final int NAME_NO_MATCH = -1; private static final int NAME_EXACT_MATCH = 0; private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER); Loading Loading @@ -131,39 +130,65 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { /** * Returns "difference" between appName and query string. appName must contain all * characters from query, in the same order. If not, returns NAME_NO_MATCH. If they do match, * returns an int value representing how different they are, NAME_EXACT_MATCH means they match * perfectly, and larger values means they are less similar. * characters from query as a prefix to a word, in the same order. * If not, returns NAME_NO_MATCH. * If they do match, returns an int value representing how different they are, * and larger values means they are less similar. * <p/> * Example: * appName: Abcde, query: Abcde, Returns {@link #NAME_EXACT_MATCH} * appName: Abcde, query: ade, Returns 2 * appName: Abcde, query: ae, Returns 3 * appName: Abcde, query: ea, Returns NAME_NO_MATCH * appName: Abcde, query: Abcde, Returns 0 * appName: Abcde, query: abc, Returns 2 * appName: Abcde, query: ab, Returns 3 * appName: Abcde, query: bc, Returns NAME_NO_MATCH * appName: Abcde, query: xyz, Returns NAME_NO_MATCH * appName: Abc de, query: de, Returns 4 */ private int getWordDifference(String appName, String query) { if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) { return NAME_NO_MATCH; } final char[] queryTokens = query.toString().toLowerCase().toCharArray(); final char[] valueText = appName.toLowerCase().toCharArray(); if (queryTokens.length > valueText.length) { final char[] queryTokens = query.toLowerCase().toCharArray(); final char[] appTokens = appName.toLowerCase().toCharArray(); final int appLength = appTokens.length; if (queryTokens.length > appLength) { return NAME_NO_MATCH; } int i = 0; int j = 0; while (i < valueText.length && j < queryTokens.length) { if (valueText[i++] == queryTokens[j]) { j++; int j; while (i < appLength) { j = 0; // Currently matching a prefix while ((i + j < appLength) && (queryTokens[j] == appTokens[i + j])) { // Matched the entire query if (++j >= queryTokens.length) { // Use the diff in length as a proxy of how close the 2 words match. // Value range from 0 to infinity. return appLength - queryTokens.length; } } if (j != queryTokens.length) { i += j; // Remaining string is longer that the query or we have search the whole app name. if (queryTokens.length > appLength - i) { return NAME_NO_MATCH; } // Use the diff in length as a proxy of how close the 2 words match. Value range from 0 // to infinity. return valueText.length - queryTokens.length; // This is the first index where app name and query name are different // Find the next space in the app name or the end of the app name. while ((i < appLength) && (!Character.isWhitespace(appTokens[i++]))) ; // Find the start of the next word while ((i < appLength) && !(Character.isLetter(appTokens[i]) || Character.isDigit(appTokens[i]))) { // Increment in body because we cannot guarantee which condition was true i++; } } return NAME_NO_MATCH; } private List<String> getBreadCrumb() { Loading tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java +143 −0 Original line number Diff line number Diff line Loading @@ -188,4 +188,147 @@ public class InstalledAppResultLoaderTest { // Then partial match assertThat(results.get(1).title).isNotEqualTo(query); } @Test public void query_normalWord_MatchPrefix() { final String query = "ba"; final String packageName = "Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_CapitalCase_DoestMatchSecondWord() { final String query = "Apples"; final String packageName = "BananasApples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } @Test public void query_TwoWords_MatchesFirstWord() { final String query = "Banana"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_TwoWords_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_ThreeWords_MatchesThirdWord() { final String query = "Pear"; final String packageName = "Bananas Apples Pears"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_DoubleSpacedWords_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_SpecialChar_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas & Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_TabSeparated_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas\tApples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_LeadingNumber_MatchesWord() { final String query = "4"; final String packageName = "4Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_FirstWordPrefixOfQuery_NoMatch() { final String query = "Bananass"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } @Test public void query_QueryLongerThanAppName_NoMatch() { final String query = "BananasApples"; final String packageName = "Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } } Loading
src/com/android/settings/search2/InstalledAppResultLoader.java +46 −21 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import java.util.List; public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { private static final int NAME_NO_MATCH = -1; private static final int NAME_EXACT_MATCH = 0; private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER); Loading Loading @@ -131,39 +130,65 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> { /** * Returns "difference" between appName and query string. appName must contain all * characters from query, in the same order. If not, returns NAME_NO_MATCH. If they do match, * returns an int value representing how different they are, NAME_EXACT_MATCH means they match * perfectly, and larger values means they are less similar. * characters from query as a prefix to a word, in the same order. * If not, returns NAME_NO_MATCH. * If they do match, returns an int value representing how different they are, * and larger values means they are less similar. * <p/> * Example: * appName: Abcde, query: Abcde, Returns {@link #NAME_EXACT_MATCH} * appName: Abcde, query: ade, Returns 2 * appName: Abcde, query: ae, Returns 3 * appName: Abcde, query: ea, Returns NAME_NO_MATCH * appName: Abcde, query: Abcde, Returns 0 * appName: Abcde, query: abc, Returns 2 * appName: Abcde, query: ab, Returns 3 * appName: Abcde, query: bc, Returns NAME_NO_MATCH * appName: Abcde, query: xyz, Returns NAME_NO_MATCH * appName: Abc de, query: de, Returns 4 */ private int getWordDifference(String appName, String query) { if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) { return NAME_NO_MATCH; } final char[] queryTokens = query.toString().toLowerCase().toCharArray(); final char[] valueText = appName.toLowerCase().toCharArray(); if (queryTokens.length > valueText.length) { final char[] queryTokens = query.toLowerCase().toCharArray(); final char[] appTokens = appName.toLowerCase().toCharArray(); final int appLength = appTokens.length; if (queryTokens.length > appLength) { return NAME_NO_MATCH; } int i = 0; int j = 0; while (i < valueText.length && j < queryTokens.length) { if (valueText[i++] == queryTokens[j]) { j++; int j; while (i < appLength) { j = 0; // Currently matching a prefix while ((i + j < appLength) && (queryTokens[j] == appTokens[i + j])) { // Matched the entire query if (++j >= queryTokens.length) { // Use the diff in length as a proxy of how close the 2 words match. // Value range from 0 to infinity. return appLength - queryTokens.length; } } if (j != queryTokens.length) { i += j; // Remaining string is longer that the query or we have search the whole app name. if (queryTokens.length > appLength - i) { return NAME_NO_MATCH; } // Use the diff in length as a proxy of how close the 2 words match. Value range from 0 // to infinity. return valueText.length - queryTokens.length; // This is the first index where app name and query name are different // Find the next space in the app name or the end of the app name. while ((i < appLength) && (!Character.isWhitespace(appTokens[i++]))) ; // Find the start of the next word while ((i < appLength) && !(Character.isLetter(appTokens[i]) || Character.isDigit(appTokens[i]))) { // Increment in body because we cannot guarantee which condition was true i++; } } return NAME_NO_MATCH; } private List<String> getBreadCrumb() { Loading
tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java +143 −0 Original line number Diff line number Diff line Loading @@ -188,4 +188,147 @@ public class InstalledAppResultLoaderTest { // Then partial match assertThat(results.get(1).title).isNotEqualTo(query); } @Test public void query_normalWord_MatchPrefix() { final String query = "ba"; final String packageName = "Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_CapitalCase_DoestMatchSecondWord() { final String query = "Apples"; final String packageName = "BananasApples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } @Test public void query_TwoWords_MatchesFirstWord() { final String query = "Banana"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_TwoWords_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_ThreeWords_MatchesThirdWord() { final String query = "Pear"; final String packageName = "Bananas Apples Pears"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_DoubleSpacedWords_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_SpecialChar_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas & Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_TabSeparated_MatchesSecondWord() { final String query = "Apple"; final String packageName = "Bananas\tApples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_LeadingNumber_MatchesWord() { final String query = "4"; final String packageName = "4Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(1); } @Test public void query_FirstWordPrefixOfQuery_NoMatch() { final String query = "Bananass"; final String packageName = "Bananas Apples"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } @Test public void query_QueryLongerThanAppName_NoMatch() { final String query = "BananasApples"; final String packageName = "Bananas"; when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */, 0 /* targetSdkVersion */))); mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query); assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } }