Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create asynchronous ContentProvider for Actionbar SearchView

I have a SearchView in my ActionBar which is connected with a ContentProvider to give search suggestions. These suggestions do not come from a DB (as usual with ContentProvider), but from a web service. That's why I have to handle the Cursor of the ContentProvider asyncronously. My code works so far, but the search suggestions are always one letter "behind":

After I enter "the", I get all results from the previous search => "th"

After I enter "they", I get all results from the previous search => "the"

How can I tell the SearchView that the Cursor has new results in it? I looked into ContentObserver and ContentResolver().notifyChange(), but they are not really possible to use in context of the SearchView.

Here's my code so far. The important part is in the onResponse-callback of the ContentProvider. I create a new MatrixCursor and use it to override the member MatrixCursor.

AutocompleteSuggestionProvider extends ContentProvider

@Override
public Cursor query(final Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

    String query = selectionArgs[0];

    mNetworkHelper.startAutoCompleteRequest(
        selectionArgs[0],
        SuggestionCategory.EVERYTHING,
        new Response.Listener<AutoCompleteResponse>() {


            /**
             * This is the callback for a successful web service request
             */
            @Override
            public void onResponse(AutoCompleteResponse response) {

                MatrixCursor nCursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 10);
                List<String> suggestions = response.getResults();

                // transfrom suggestions to MatrixCursor
                for (int i = 0; i < suggestions.size() && i < 10; i++) 
                    nCursor.addRow(new String[]{String.valueOf(i), suggestions.get(i)});
                }

                // update cursor
                mAsyncCursor = nCursor;
            }
        }, 

        /**
         * This is the callback for a errornous web service request
         */
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(getContext(), "Fehler", Toast.LENGTH_SHORT).show();
            }
        }
    );
    return mAsyncCursor;
}

AndroidManifest

<activity
        android:name=".activities.MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>

        <meta-data android:name="android.app.default_searchable" android:value=".MainActivity" />
        <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>

    </activity>

    <provider
        android:name=".provider.AutocompleteSuggestionProvider"
        android:authorities="my.package.provider.AutocompleteSuggestion"
        android:exported="false" />

searchable.xml

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/app_name"
        android:hint="@string/search_hint"
        android:searchSettingsDescription="@string/search_settings"

        android:searchSuggestAuthority="my.package.provider.AutocompleteSuggestion"
        android:searchSuggestIntentAction="android.intent.action.VIEW"
        android:searchSuggestSelection=" ?"
        android:searchSuggestThreshold="2" >
</searchable>
like image 555
muetzenflo Avatar asked Jul 01 '14 19:07

muetzenflo


1 Answers

I found the solution. The most important thing to know ist that the query-method of a ContentProvider does NOT run on the UI-thread. Therefore we can do a synchronous HTTP call. Since every sane person uses Volley these days you have to do this call like this:

String url = NetworkHelper.buildRequestUrl(selectionArgs[0], SuggestionCategory.EVERYTHING, RequestType.AUTOCOMPLETE);
RequestFuture<JSONArray> future = RequestFuture.newFuture();
JsonArrayRequest request = new JsonArrayRequest(url, future, future);

mNetworkHelper.getRequestQueue().add(request);

// this does not run on the UI thread, so we can make a synchronous HTTP request
try {
  JSONArray suggestions = future.get();
  MatrixCursor resultCursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 10);

  for (int i = 0; i < suggestions.length() && i < 10; i++) {
    resultCursor.addRow(new String[]{String.valueOf(i), suggestions.get(i).toString()});
  }

  return resultCursor;

} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
} catch (JSONException e) {
  e.printStackTrace();
}

This way everything works fine.

like image 154
muetzenflo Avatar answered Sep 27 '22 23:09

muetzenflo