Make saveRecentQuery() async.
Bug: 3163612 Change-Id: Idd3c1925e0f1dc3272dd1303d8f2907c5c5fca8b
This commit is contained in:
@@ -20,36 +20,35 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SearchRecentSuggestionsProvider;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* This is a utility class providing access to
|
||||
* This is a utility class providing access to
|
||||
* {@link android.content.SearchRecentSuggestionsProvider}.
|
||||
*
|
||||
*
|
||||
* <p>Unlike some utility classes, this one must be instantiated and properly initialized, so that
|
||||
* it can be configured to operate with the search suggestions provider that you have created.
|
||||
*
|
||||
*
|
||||
* <p>Typically, you will do this in your searchable activity, each time you receive an incoming
|
||||
* {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent. The code to record each
|
||||
* incoming query is as follows:
|
||||
* <pre class="prettyprint">
|
||||
* SearchSuggestions suggestions = new SearchSuggestions(this,
|
||||
* SearchSuggestions suggestions = new SearchSuggestions(this,
|
||||
* MySuggestionsProvider.AUTHORITY, MySuggestionsProvider.MODE);
|
||||
* suggestions.saveRecentQuery(queryString, null);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>For a working example, see SearchSuggestionSampleProvider and SearchQueryResults in
|
||||
* samples/ApiDemos/app.
|
||||
*/
|
||||
public class SearchRecentSuggestions {
|
||||
// debugging support
|
||||
private static final String LOG_TAG = "SearchSuggestions";
|
||||
// DELETE ME (eventually)
|
||||
private static final int DBG_SUGGESTION_TIMESTAMPS = 0;
|
||||
|
||||
|
||||
// This is a superset of all possible column names (need not all be in table)
|
||||
private static class SuggestionColumns implements BaseColumns {
|
||||
public static final String DISPLAY1 = "display1";
|
||||
@@ -57,27 +56,28 @@ public class SearchRecentSuggestions {
|
||||
public static final String QUERY = "query";
|
||||
public static final String DATE = "date";
|
||||
}
|
||||
|
||||
|
||||
/* if you change column order you must also change indices below */
|
||||
/**
|
||||
* This is the database projection that can be used to view saved queries, when
|
||||
* configured for one-line operation.
|
||||
*/
|
||||
public static final String[] QUERIES_PROJECTION_1LINE = new String[] {
|
||||
SuggestionColumns._ID,
|
||||
SuggestionColumns._ID,
|
||||
SuggestionColumns.DATE,
|
||||
SuggestionColumns.QUERY,
|
||||
SuggestionColumns.QUERY,
|
||||
SuggestionColumns.DISPLAY1,
|
||||
};
|
||||
|
||||
/* if you change column order you must also change indices below */
|
||||
/**
|
||||
* This is the database projection that can be used to view saved queries, when
|
||||
* configured for two-line operation.
|
||||
*/
|
||||
public static final String[] QUERIES_PROJECTION_2LINE = new String[] {
|
||||
SuggestionColumns._ID,
|
||||
SuggestionColumns._ID,
|
||||
SuggestionColumns.DATE,
|
||||
SuggestionColumns.QUERY,
|
||||
SuggestionColumns.QUERY,
|
||||
SuggestionColumns.DISPLAY1,
|
||||
SuggestionColumns.DISPLAY2,
|
||||
};
|
||||
@@ -91,11 +91,6 @@ public class SearchRecentSuggestions {
|
||||
public static final int QUERIES_PROJECTION_DISPLAY1_INDEX = 3;
|
||||
/** Index into the provided query projections. For use with Cursor.update methods. */
|
||||
public static final int QUERIES_PROJECTION_DISPLAY2_INDEX = 4; // only when 2line active
|
||||
|
||||
/* columns needed to determine whether to truncate history */
|
||||
private static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] {
|
||||
SuggestionColumns._ID, SuggestionColumns.DATE
|
||||
};
|
||||
|
||||
/*
|
||||
* Set a cap on the count of items in the suggestions table, to
|
||||
@@ -103,73 +98,90 @@ public class SearchRecentSuggestions {
|
||||
* cap when/if db/layout performance improvements are made.
|
||||
*/
|
||||
private static final int MAX_HISTORY_COUNT = 250;
|
||||
|
||||
|
||||
// client-provided configuration values
|
||||
private Context mContext;
|
||||
private String mAuthority;
|
||||
private boolean mTwoLineDisplay;
|
||||
private Uri mSuggestionsUri;
|
||||
private String[] mQueriesProjection;
|
||||
private final Context mContext;
|
||||
private final String mAuthority;
|
||||
private final boolean mTwoLineDisplay;
|
||||
private final Uri mSuggestionsUri;
|
||||
|
||||
/** Released once per completion of async write. Used for tests. */
|
||||
private static final Semaphore sWritesInProgress = new Semaphore(0);
|
||||
|
||||
/**
|
||||
* Although provider utility classes are typically static, this one must be constructed
|
||||
* because it needs to be initialized using the same values that you provided in your
|
||||
* {@link android.content.SearchRecentSuggestionsProvider}.
|
||||
*
|
||||
* because it needs to be initialized using the same values that you provided in your
|
||||
* {@link android.content.SearchRecentSuggestionsProvider}.
|
||||
*
|
||||
* @param authority This must match the authority that you've declared in your manifest.
|
||||
* @param mode You can use mode flags here to determine certain functional aspects of your
|
||||
* database. Note, this value should not change from run to run, because when it does change,
|
||||
* your suggestions database may be wiped.
|
||||
*
|
||||
*
|
||||
* @see android.content.SearchRecentSuggestionsProvider
|
||||
* @see android.content.SearchRecentSuggestionsProvider#setupSuggestions
|
||||
*/
|
||||
public SearchRecentSuggestions(Context context, String authority, int mode) {
|
||||
if (TextUtils.isEmpty(authority) ||
|
||||
if (TextUtils.isEmpty(authority) ||
|
||||
((mode & SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES) == 0)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// unpack mode flags
|
||||
mTwoLineDisplay = (0 != (mode & SearchRecentSuggestionsProvider.DATABASE_MODE_2LINES));
|
||||
|
||||
|
||||
// saved values
|
||||
mContext = context;
|
||||
mAuthority = new String(authority);
|
||||
|
||||
|
||||
// derived values
|
||||
mSuggestionsUri = Uri.parse("content://" + mAuthority + "/suggestions");
|
||||
|
||||
if (mTwoLineDisplay) {
|
||||
mQueriesProjection = QUERIES_PROJECTION_2LINE;
|
||||
} else {
|
||||
mQueriesProjection = QUERIES_PROJECTION_1LINE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query to the recent queries list.
|
||||
*
|
||||
* Add a query to the recent queries list. Returns immediately, performing the save
|
||||
* in the background.
|
||||
*
|
||||
* @param queryString The string as typed by the user. This string will be displayed as
|
||||
* the suggestion, and if the user clicks on the suggestion, this string will be sent to your
|
||||
* searchable activity (as a new search query).
|
||||
* @param line2 If you have configured your recent suggestions provider with
|
||||
* {@link android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, you can
|
||||
* @param line2 If you have configured your recent suggestions provider with
|
||||
* {@link android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, you can
|
||||
* pass a second line of text here. It will be shown in a smaller font, below the primary
|
||||
* suggestion. When typing, matches in either line of text will be displayed in the list.
|
||||
* If you did not configure two-line mode, or if a given suggestion does not have any
|
||||
* additional text to display, you can pass null here.
|
||||
*/
|
||||
public void saveRecentQuery(String queryString, String line2) {
|
||||
public void saveRecentQuery(final String queryString, final String line2) {
|
||||
if (TextUtils.isEmpty(queryString)) {
|
||||
return;
|
||||
}
|
||||
if (!mTwoLineDisplay && !TextUtils.isEmpty(line2)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
|
||||
new Thread("saveRecentQuery") {
|
||||
@Override
|
||||
public void run() {
|
||||
saveRecentQueryBlocking(queryString, line2);
|
||||
sWritesInProgress.release();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
// Visible for testing.
|
||||
void waitForSave() {
|
||||
// Acquire writes semaphore until there is nothing available.
|
||||
// This is to clean up after any previous callers to saveRecentQuery
|
||||
// who did not also call waitForSave().
|
||||
do {
|
||||
sWritesInProgress.acquireUninterruptibly();
|
||||
} while (sWritesInProgress.availablePermits() > 0);
|
||||
}
|
||||
|
||||
private void saveRecentQueryBlocking(String queryString, String line2) {
|
||||
ContentResolver cr = mContext.getContentResolver();
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
|
||||
// Use content resolver (not cursor) to insert/update this query
|
||||
try {
|
||||
ContentValues values = new ContentValues();
|
||||
@@ -183,14 +195,14 @@ public class SearchRecentSuggestions {
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(LOG_TAG, "saveRecentQuery", e);
|
||||
}
|
||||
|
||||
|
||||
// Shorten the list (if it has become too long)
|
||||
truncateHistory(cr, MAX_HISTORY_COUNT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Completely delete the history. Use this call to implement a "clear history" UI.
|
||||
*
|
||||
*
|
||||
* Any application that implements search suggestions based on previous actions (such as
|
||||
* recent queries, page/items viewed, etc.) should provide a way for the user to clear the
|
||||
* history. This gives the user a measure of privacy, if they do not wish for their recent
|
||||
@@ -203,7 +215,7 @@ public class SearchRecentSuggestions {
|
||||
|
||||
/**
|
||||
* Reduces the length of the history table, to prevent it from growing too large.
|
||||
*
|
||||
*
|
||||
* @param cr Convenience copy of the content resolver.
|
||||
* @param maxEntries Max entries to leave in the table. 0 means remove all entries.
|
||||
*/
|
||||
@@ -211,7 +223,7 @@ public class SearchRecentSuggestions {
|
||||
if (maxEntries < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// null means "delete all". otherwise "delete but leave n newest"
|
||||
String selection = null;
|
||||
|
||||
@@ -14,72 +14,58 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content;
|
||||
package android.provider;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
import android.test.ProviderTestCase2;
|
||||
import android.test.suitebuilder.annotation.Suppress;
|
||||
|
||||
/**
|
||||
* Very simple provider that I can instantiate right here.
|
||||
*/
|
||||
class TestProvider extends SearchRecentSuggestionsProvider {
|
||||
final static String AUTHORITY = "android.content.TestProvider";
|
||||
final static int MODE = DATABASE_MODE_QUERIES + DATABASE_MODE_2LINES;
|
||||
|
||||
public TestProvider() {
|
||||
super();
|
||||
setupSuggestions(AUTHORITY, MODE);
|
||||
}
|
||||
}
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
/**
|
||||
* ProviderTestCase that performs unit tests of SearchRecentSuggestionsProvider.
|
||||
*
|
||||
*
|
||||
* You can run this test in isolation via the commands:
|
||||
*
|
||||
*
|
||||
* $ (cd tests/FrameworkTests/ && mm) && adb sync
|
||||
* $ adb shell am instrument -w \
|
||||
* -e class android.content.SearchRecentSuggestionsProviderTest
|
||||
* -e class android.provider.SearchRecentSuggestionsProviderTest
|
||||
* com.android.frameworktest.tests/android.test.InstrumentationTestRunner
|
||||
*/
|
||||
// Suppress these until bug http://b/issue?id=1416586 is fixed.
|
||||
@Suppress
|
||||
@MediumTest
|
||||
public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestProvider> {
|
||||
|
||||
// Elements prepared by setUp()
|
||||
SearchRecentSuggestions mSearchHelper;
|
||||
|
||||
|
||||
public SearchRecentSuggestionsProviderTest() {
|
||||
super(TestProvider.class, TestProvider.AUTHORITY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* During setup, grab a helper for DB access
|
||||
* During setup, grab a helper for DB access
|
||||
*/
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
|
||||
// Use the recent suggestions helper. As long as we pass in our isolated context,
|
||||
// it should correctly access the provider under test.
|
||||
mSearchHelper = new SearchRecentSuggestions(getMockContext(),
|
||||
mSearchHelper = new SearchRecentSuggestions(getMockContext(),
|
||||
TestProvider.AUTHORITY, TestProvider.MODE);
|
||||
|
||||
// test for empty database at setup time
|
||||
checkOpenCursorCount(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple test to see if we can instantiate the whole mess.
|
||||
*/
|
||||
public void testSetup() {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple test to see if we can write and read back a single query
|
||||
*/
|
||||
@@ -87,22 +73,23 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String TEST_LINE1 = "test line 1";
|
||||
final String TEST_LINE2 = "test line 2";
|
||||
mSearchHelper.saveRecentQuery(TEST_LINE1, TEST_LINE2);
|
||||
|
||||
mSearchHelper.waitForSave();
|
||||
|
||||
// make sure that there are is exactly one entry returned by a non-filtering cursor
|
||||
checkOpenCursorCount(1);
|
||||
|
||||
|
||||
// test non-filtering cursor for correct entry
|
||||
checkResultCounts(null, 1, 1, TEST_LINE1, TEST_LINE2);
|
||||
|
||||
|
||||
// test filtering cursor for correct entry
|
||||
checkResultCounts(TEST_LINE1, 1, 1, TEST_LINE1, TEST_LINE2);
|
||||
checkResultCounts(TEST_LINE2, 1, 1, TEST_LINE1, TEST_LINE2);
|
||||
|
||||
|
||||
// test that a different filter returns zero results
|
||||
checkResultCounts("bad filter", 0, 0, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Simple test to see if we can write and read back a diverse set of queries
|
||||
*/
|
||||
public void testMixedQueries() {
|
||||
@@ -111,19 +98,19 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String TEST_GROUP_2 = "test ";
|
||||
final String TEST_LINE2 = "line2 ";
|
||||
final int GROUP_COUNT = 10;
|
||||
|
||||
|
||||
writeEntries(GROUP_COUNT, TEST_GROUP_1, TEST_LINE2);
|
||||
writeEntries(GROUP_COUNT, TEST_GROUP_2, TEST_LINE2);
|
||||
|
||||
|
||||
// check counts
|
||||
checkOpenCursorCount(2 * GROUP_COUNT);
|
||||
|
||||
|
||||
// check that each query returns the right result counts
|
||||
checkResultCounts(TEST_GROUP_1, GROUP_COUNT, GROUP_COUNT, null, null);
|
||||
checkResultCounts(TEST_GROUP_2, GROUP_COUNT, GROUP_COUNT, null, null);
|
||||
checkResultCounts(TEST_LINE2, 2 * GROUP_COUNT, 2 * GROUP_COUNT, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that the reordering code works properly. The most recently injected queries
|
||||
* should replace existing queries and be sorted to the top of the list.
|
||||
@@ -134,41 +121,41 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String GROUP_1_QUERY = "group1 ";
|
||||
final String GROUP_1_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_1_COUNT, GROUP_1_QUERY, GROUP_1_LINE2);
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(GROUP_1_COUNT);
|
||||
|
||||
// guarantee that group 1 has older timestamps
|
||||
writeDelay();
|
||||
|
||||
|
||||
// next we'll add 10 entries named "group2 x"
|
||||
final int GROUP_2_COUNT = 10;
|
||||
final String GROUP_2_QUERY = "group2 ";
|
||||
final String GROUP_2_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_2_COUNT, GROUP_2_QUERY, GROUP_2_LINE2);
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(GROUP_1_COUNT + GROUP_2_COUNT);
|
||||
|
||||
// guarantee that group 2 has older timestamps
|
||||
writeDelay();
|
||||
|
||||
|
||||
// now refresh 5 of the 10 from group 1
|
||||
// change line2 so they can be more easily tracked
|
||||
final int GROUP_3_COUNT = 5;
|
||||
final String GROUP_3_QUERY = GROUP_1_QUERY;
|
||||
final String GROUP_3_LINE2 = "refreshed ";
|
||||
writeEntries(GROUP_3_COUNT, GROUP_3_QUERY, GROUP_3_LINE2);
|
||||
|
||||
|
||||
// confirm that the total didn't change (those were replacements, not adds)
|
||||
checkOpenCursorCount(GROUP_1_COUNT + GROUP_2_COUNT);
|
||||
|
||||
|
||||
// confirm that the are now 5 in group 1, 10 in group 2, and 5 in group 3
|
||||
int newGroup1Count = GROUP_1_COUNT - GROUP_3_COUNT;
|
||||
checkResultCounts(GROUP_1_QUERY, newGroup1Count, newGroup1Count, null, GROUP_1_LINE2);
|
||||
checkResultCounts(GROUP_2_QUERY, GROUP_2_COUNT, GROUP_2_COUNT, null, null);
|
||||
checkResultCounts(GROUP_3_QUERY, GROUP_3_COUNT, GROUP_3_COUNT, null, GROUP_3_LINE2);
|
||||
|
||||
|
||||
// finally, spot check that the right groups are in the right places
|
||||
// the ordering should be group 3 (newest), group 2, group 1 (oldest)
|
||||
Cursor c = getQueryCursor(null);
|
||||
@@ -199,14 +186,14 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
c.move(newGroup1Count - 1);
|
||||
assertTrue("group 1 not in expected position after reordering",
|
||||
checkRow(c, colQuery, colDisplay1, colDisplay2, GROUP_1_QUERY, GROUP_1_LINE2));
|
||||
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that the pruning code works properly, The database should not go beyond 250 entries,
|
||||
* and the oldest entries should always be discarded first.
|
||||
*
|
||||
* and the oldest entries should always be discarded first.
|
||||
*
|
||||
* TODO: This is a slow test, do we have annotation for that?
|
||||
*/
|
||||
public void testPruning() {
|
||||
@@ -215,29 +202,29 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String GROUP_1_QUERY = "group1 ";
|
||||
final String GROUP_1_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_1_COUNT, GROUP_1_QUERY, GROUP_1_LINE2);
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(GROUP_1_COUNT);
|
||||
|
||||
// guarantee that group 1 has older timestamps (and will be pruned first)
|
||||
writeDelay();
|
||||
|
||||
|
||||
// next we'll add 200 entries named "group2 x"
|
||||
final int GROUP_2_COUNT = 200;
|
||||
final String GROUP_2_QUERY = "group2 ";
|
||||
final String GROUP_2_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_2_COUNT, GROUP_2_QUERY, GROUP_2_LINE2);
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(GROUP_1_COUNT + GROUP_2_COUNT);
|
||||
|
||||
|
||||
// Finally we'll add 10 more entries named "group3 x"
|
||||
// These should push out 10 entries from group 1
|
||||
final int GROUP_3_COUNT = 10;
|
||||
final String GROUP_3_QUERY = "group3 ";
|
||||
final String GROUP_3_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_3_COUNT, GROUP_3_QUERY, GROUP_3_LINE2);
|
||||
|
||||
|
||||
// total should still be 250
|
||||
checkOpenCursorCount(GROUP_1_COUNT + GROUP_2_COUNT);
|
||||
|
||||
@@ -247,7 +234,7 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
checkResultCounts(GROUP_2_QUERY, GROUP_2_COUNT, GROUP_2_COUNT, null, null);
|
||||
checkResultCounts(GROUP_3_QUERY, GROUP_3_COUNT, GROUP_3_COUNT, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that the clear history code works properly.
|
||||
*/
|
||||
@@ -257,23 +244,23 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String GROUP_1_QUERY = "group1 ";
|
||||
final String GROUP_1_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_1_COUNT, GROUP_1_QUERY, GROUP_1_LINE2);
|
||||
|
||||
|
||||
// next we'll add 10 entries named "group2 x"
|
||||
final int GROUP_2_COUNT = 10;
|
||||
final String GROUP_2_QUERY = "group2 ";
|
||||
final String GROUP_2_LINE2 = "line2 ";
|
||||
writeEntries(GROUP_2_COUNT, GROUP_2_QUERY, GROUP_2_LINE2);
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(GROUP_1_COUNT + GROUP_2_COUNT);
|
||||
|
||||
// delete all
|
||||
mSearchHelper.clearHistory();
|
||||
|
||||
|
||||
// check totals
|
||||
checkOpenCursorCount(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write a sequence of queries into the database, with incrementing counters in the strings.
|
||||
*/
|
||||
@@ -282,9 +269,10 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
final String line1 = line1Base + i;
|
||||
final String line2 = line2Base + i;
|
||||
mSearchHelper.saveRecentQuery(line1, line2);
|
||||
mSearchHelper.waitForSave();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A very slight delay to ensure that successive groups of queries in the DB cannot
|
||||
* have the same timestamp.
|
||||
@@ -296,11 +284,11 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
fail("Interrupted sleep.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access an "open" (no selection) suggestions cursor and confirm that it has the specified
|
||||
* number of entries.
|
||||
*
|
||||
*
|
||||
* @param expectCount The expected number of entries returned by the cursor.
|
||||
*/
|
||||
private void checkOpenCursorCount(int expectCount) {
|
||||
@@ -308,10 +296,10 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
assertEquals(expectCount, c.getCount());
|
||||
c.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set up a filter cursor and then scan it for specific results.
|
||||
*
|
||||
*
|
||||
* @param queryString The query string to apply.
|
||||
* @param minRows The minimum number of matching rows that must be found.
|
||||
* @param maxRows The maximum number of matching rows that must be found.
|
||||
@@ -320,12 +308,12 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
*/
|
||||
private void checkResultCounts(String queryString, int minRows, int maxRows,
|
||||
String matchDisplay1, String matchDisplay2) {
|
||||
|
||||
|
||||
// get the cursor and apply sanity checks to result
|
||||
Cursor c = getQueryCursor(queryString);
|
||||
assertNotNull(c);
|
||||
assertTrue("Insufficient rows in filtered cursor", c.getCount() >= minRows);
|
||||
|
||||
|
||||
// look for minimum set of columns (note, display2 is optional)
|
||||
int colQuery = c.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_QUERY);
|
||||
int colDisplay1 = c.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_TEXT_1);
|
||||
@@ -337,20 +325,20 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
while (!c.isAfterLast()) {
|
||||
if (checkRow(c, colQuery, colDisplay1, colDisplay2, matchDisplay1, matchDisplay2)) {
|
||||
foundRows++;
|
||||
}
|
||||
}
|
||||
c.moveToNext();
|
||||
}
|
||||
|
||||
// now check the results
|
||||
assertTrue(minRows <= foundRows);
|
||||
assertTrue(foundRows <= maxRows);
|
||||
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check a single row for equality with target strings.
|
||||
*
|
||||
*
|
||||
* @param c The cursor, already moved to the row
|
||||
* @param colQuery The column # containing the query. The query must match display1.
|
||||
* @param colDisp1 The column # containing display line 1.
|
||||
@@ -365,7 +353,7 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
String query = c.getString(colQuery);
|
||||
String display1 = c.getString(colDisp1);
|
||||
String display2 = (colDisp2 >= 0) ? c.getString(colDisp2) : null;
|
||||
|
||||
|
||||
assertEquals(query, display1);
|
||||
boolean result = true;
|
||||
if (matchDisplay1 != null) {
|
||||
@@ -374,13 +362,13 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
if (matchDisplay2 != null) {
|
||||
result = result && (display2 != null) && display2.startsWith(matchDisplay2);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a query cursor in a manner like the search dialog would.
|
||||
*
|
||||
*
|
||||
* @param queryString The search string, or, null for "all"
|
||||
* @return Returns a cursor, or null if there was some problem. Be sure to close the cursor
|
||||
* when done with it.
|
||||
@@ -388,7 +376,7 @@ public class SearchRecentSuggestionsProviderTest extends ProviderTestCase2<TestP
|
||||
private Cursor getQueryCursor(String queryString) {
|
||||
ContentResolver cr = getMockContext().getContentResolver();
|
||||
|
||||
String uriStr = "content://" + TestProvider.AUTHORITY +
|
||||
String uriStr = "content://" + TestProvider.AUTHORITY +
|
||||
'/' + SearchManager.SUGGEST_URI_PATH_QUERY;
|
||||
Uri contentUri = Uri.parse(uriStr);
|
||||
|
||||
33
core/tests/coretests/src/android/provider/TestProvider.java
Normal file
33
core/tests/coretests/src/android/provider/TestProvider.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.provider;
|
||||
|
||||
import android.content.SearchRecentSuggestionsProvider;
|
||||
|
||||
/**
|
||||
* Very simple provider that I can instantiate right here.
|
||||
*/
|
||||
public class TestProvider extends SearchRecentSuggestionsProvider {
|
||||
final static String AUTHORITY = "android.provider.TestProvider";
|
||||
final static int MODE = DATABASE_MODE_QUERIES + DATABASE_MODE_2LINES;
|
||||
|
||||
public TestProvider() {
|
||||
super();
|
||||
setupSuggestions(AUTHORITY, MODE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user