Installed App loader only matches on prefixes
Change-Id: Iefc6d681a1b616acf3c1354ff4d0f6c2c268895e Fixes:34688403 Test: make RunSettingsRoboTests
This commit is contained in:
@@ -45,7 +45,6 @@ import java.util.List;
|
|||||||
public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
|
public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
|
||||||
|
|
||||||
private static final int NAME_NO_MATCH = -1;
|
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)
|
private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN)
|
||||||
.addCategory(Intent.CATEGORY_LAUNCHER);
|
.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
|
|
||||||
@@ -131,39 +130,65 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns "difference" between appName and query string. appName must contain all
|
* 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,
|
* characters from query as a prefix to a word, in the same order.
|
||||||
* returns an int value representing how different they are, NAME_EXACT_MATCH means they match
|
* If not, returns NAME_NO_MATCH.
|
||||||
* perfectly, and larger values means they are less similar.
|
* If they do match, returns an int value representing how different they are,
|
||||||
|
* and larger values means they are less similar.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Example:
|
* Example:
|
||||||
* appName: Abcde, query: Abcde, Returns {@link #NAME_EXACT_MATCH}
|
* appName: Abcde, query: Abcde, Returns 0
|
||||||
* appName: Abcde, query: ade, Returns 2
|
* appName: Abcde, query: abc, Returns 2
|
||||||
* appName: Abcde, query: ae, Returns 3
|
* appName: Abcde, query: ab, Returns 3
|
||||||
* appName: Abcde, query: ea, Returns NAME_NO_MATCH
|
* appName: Abcde, query: bc, Returns NAME_NO_MATCH
|
||||||
* appName: Abcde, query: xyz, 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) {
|
private int getWordDifference(String appName, String query) {
|
||||||
if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) {
|
if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) {
|
||||||
return NAME_NO_MATCH;
|
return NAME_NO_MATCH;
|
||||||
}
|
}
|
||||||
final char[] queryTokens = query.toString().toLowerCase().toCharArray();
|
|
||||||
final char[] valueText = appName.toLowerCase().toCharArray();
|
final char[] queryTokens = query.toLowerCase().toCharArray();
|
||||||
if (queryTokens.length > valueText.length) {
|
final char[] appTokens = appName.toLowerCase().toCharArray();
|
||||||
|
final int appLength = appTokens.length;
|
||||||
|
if (queryTokens.length > appLength) {
|
||||||
return NAME_NO_MATCH;
|
return NAME_NO_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j;
|
||||||
while (i < valueText.length && j < queryTokens.length) {
|
|
||||||
if (valueText[i++] == queryTokens[j]) {
|
while (i < appLength) {
|
||||||
j++;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j != queryTokens.length) {
|
return NAME_NO_MATCH;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getBreadCrumb() {
|
private List<String> getBreadCrumb() {
|
||||||
|
@@ -188,4 +188,147 @@ public class InstalledAppResultLoaderTest {
|
|||||||
// Then partial match
|
// Then partial match
|
||||||
assertThat(results.get(1).title).isNotEqualTo(query);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user