1

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
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Dieglock
  • 169
  • 1
  • 16

1 Answers1

0

The app was stopping because the SearchManager was not implemented properly.

There is no easy way to manage searches without the SearchManager. At least not for me. And, on the other hand, it is a fully functional already search engine, why use other tool?

Dieglock
  • 169
  • 1
  • 16