I have an app that is a dictionary. At the onStart method, a FragmentActivity calls the loader and populates a ListFragment where one can see the words that have a definition.
Until then everything works nice, but when I put any letter on the SearchView, the app stops.
I know something on suggestions path inside my app is not working. I almost bet it is the getSuggestions cursor on the provider, connected with the queryForWordsMatchesSuggestions on the database.
I am not using SearchManager because I am not sure it is necessary. Anyhow, how must my cursor query the database to get suggestions instead of app colapsing?
Should I built another cursor on the FragmentActivity that holds the loader, or just by having a proper cursor in provider is enough?
Thanks.
I will put the code for all suggestions route hopping that helps to receive an answer, an example.
Suggestions path is like this: The SearchView is implementing the OnQueryText change, as you can see on the snippet:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM );
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
public boolean onQueryTextChange(String newText) {
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
if (mCurFilter == null && newFilter == null) {
return true;
}
if (mCurFilter != null && mCurFilter.equals(newFilter)) {
return true;
}
mCurFilter = newFilter;
getLoaderManager().restartLoader(0, null, this);
return true;
}
Then, the loadermanager takes care, calling the CONTENT_URI
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(DictionaryProvider.CONTENT_URI,
Uri.encode(mCurFilter));
} else {
baseUri = DictionaryProvider.CONTENT_URI; // Why is this not working?
}
String select = "((" + DictionaryDatabase.KEY_WORD + " NOTNULL) AND ("
+ DictionaryDatabase.KEY_WORD + " != '' ))";
return new CursorLoader(getActivity(),
baseUri,
DICTIONARY_PROJECTION,
select,
null, // Select arguments
DictionaryDatabase.KEY_WORD + " COLLATE LOCALIZED ASC");
}
Then, comes the provider. (I took out cursors getWordsArray and getWord, and the same for insert, delete and update)
public class DictionaryProvider extends ContentProvider {
private static final String TAG = "DictionaryProvider";
public static String AUTHORITY = "com.dominicapps.cursorsayouno.DictionaryProvider";
public static final Uri CONTENT_URI = Uri.parse(
"content://" + AUTHORITY + "/dictionary");
public static final String WORDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
"/vnd.com.dominicapps.cursorsayouno";
public static final String DEFINITION_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +
"/vnd.com.dominicapps.cursorsayouno";
private DictionaryDatabase mDictionary;
private static final int GET_WORDS_ARRAY = 0;
private static final int GET_WORD = 1;
private static final int SUGGESTIONS = 2; // Is this needed?
private static final UriMatcher sURIMatcher = buildUriMatcher();
private static UriMatcher buildUriMatcher() {
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY, "dictionary", GET_WORDS_ARRAY);
matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
matcher.addURI(AUTHORITY, "/*", SUGGESTIONS);
return matcher;
}
@Override
public boolean onCreate() {
mDictionary = new DictionaryDatabase(getContext());
return true;
}
@Override
public Cursor query(Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder) {
switch (sURIMatcher.match(uri)) {
case GET_WORDS_ARRAY:
return getWordsArray(selection);
case GET_WORD:
return getWord(uri);
case SUGGESTIONS:
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
return getSuggestions(selectionArgs[0]);
default:
throw new IllegalArgumentException("Unknown Uri: " + uri);
}
}
private Cursor getSuggestions(String query) {
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION };
return mDictionary.queryForWordMatchesSuggestions(query, columns);
}
@Override
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case GET_WORDS_ARRAY:
return WORDS_MIME_TYPE;
case GET_WORD:
return DEFINITION_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
This is the querying cursor on the database.
public Cursor queryForWordMatchesSuggestions(String query, String[] columns) {
String selection = " rowid = ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
}
And the general querying on database:
public Cursor query(String selection, String[] selectionArgs, String[] columns) {
Log.d(TAG, "+++ query en Proceso +++");
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(FTS_VIRTUAL_TABLE);
builder.setProjectionMap(mColumnMap);
Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
columns, selection, selectionArgs, null, null, null);
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
And, this is the log:
06-17 05:31:55.576: W/dalvikvm(597): threadid=12: thread exiting with uncaught exception (group=0x409c01f8)
06-17 05:31:55.587: E/AndroidRuntime(597): FATAL EXCEPTION: AsyncTask #2
06-17 05:31:55.587: E/AndroidRuntime(597): java.lang.RuntimeException: An error occured while executing doInBackground()
06-17 05:31:55.587: E/AndroidRuntime(597): at android.os.AsyncTask$3.done(AsyncTask.java:278)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.lang.Thread.run(Thread.java:856)
06-17 05:31:55.587: E/AndroidRuntime(597): Caused by: java.lang.IllegalArgumentException: Unknown Uri: content://com.dominicapps.cursorsayouno.DictionaryProvider/dictionary/y
06-17 05:31:55.587: E/AndroidRuntime(597): at com.dominicapps.cursorsayouno.DictionaryProvider.query(DictionaryProvider.java:65)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.ContentProvider$Transport.query(ContentProvider.java:178)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.ContentResolver.query(ContentResolver.java:311)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.CursorLoader.loadInBackground(CursorLoader.java:56)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.CursorLoader.loadInBackground(CursorLoader.java:42)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:255)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:66)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:55)
06-17 05:31:55.587: E/AndroidRuntime(597): at android.os.AsyncTask$2.call(AsyncTask.java:264)
06-17 05:31:55.587: E/AndroidRuntime(597): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
06-17 05:31:55.587: E/AndroidRuntime(597): ... 4 more
06-17 05:31:56.227: W/IInputConnectionWrapper(597): showStatusIcon on inactive InputConnection
06-17 05:31:58.086: I/Process(597): Sending signal. PID: 597 SIG: 9