Search suggestions from network resource into Quick Search box
Asked Answered
C

3

17

I'm building the search in an application and need to have a way of putting the suggestions which I get from my server as a JSON-array into the list of suggestions which is displayed below the Quick Search Box.

Is there an easy way to have the quick search box read such resources?

Currently I am trying to use a ContentProvider, but the interface methods clearly indicate one is supposed to be querying a database to get the suggestions. I guess using a ContentProvider is the correct way if you are searching the data which is stored inside the application. I am not so sure however, that it is the right way if you need to query a network resource.

It makes no sense for me to save the suggestions I get from the network to a local database, as the suggestions and their hit rate will vary from time to time.

Anyone had this issue? Or can point me in the direction of a similar question? I could not find questions here on stack that mentioned network suggestions.

Calabar answered 24/11, 2010 at 8:4 Comment(2)
Take a look here: #2124243 - I think what u could do: Once user has entered text, wait N msec, then inflate a loading view below the text area (...), start a asynctask to retrieve the results, in end of asynctask replace loading view with a listview of results. (cache the listview based on the search query).Preece
I guess that is one solution, at least on how to display the results. However, I am more looking for a solution as to how you can implement a ContentProvider and add my custom suggestions like they do here: developer.android.com/guide/topics/search/…. Only that the suggestions comes from a network resource and not the data inside my application.Calabar
C
12

Found the solution on developer.android.com:

If you have suggestions that you get from a network location, you can build a cursor on the fly when you get the results from your server.

This goes inside your ContentProvider's query() method:

String[] columns = {
   BaseColumns._ID, 
   SearchManager.SUGGEST_COLUMN_TEXT_1, 
   SearchManager.SUGGEST_COLUMN_INTENT_DATA
};

MatrixCursor cursor = new MatrixCursor(columns);

for (int i = 0; i < arr.length(); i++)
{
  String[] tmp = {Integer.toString(i), arr.getString(i), arr.getString(i)};
  cursor.addRow(tmp);
}
return cursor;

The cursor is the used in the quick-search box to fill a list of suggestions.

Calabar answered 24/11, 2010 at 12:50 Comment(3)
Where do you perform the network request in the query? I am stuck since its on the UI thread.Ijssel
@Ijssel He already stated that … This goes inside your ContentProvider's query() method: …Juliennejuliet
explain in detail. I don't get it.Sheriff
B
13

Here is an example of SearchView with suggestions coming from a network service (I used Retrofit):

@Override
public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_search_activity, menu);

        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.search));

        final CursorAdapter suggestionAdapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_1,
                null,
                new String[]{SearchManager.SUGGEST_COLUMN_TEXT_1},
                new int[]{android.R.id.text1},
                0);
        final List<String> suggestions = new ArrayList<>();

        searchView.setSuggestionsAdapter(suggestionAdapter);
        searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
            @Override
            public boolean onSuggestionSelect(int position) {
                return false;
            }

            @Override
            public boolean onSuggestionClick(int position) {
                searchView.setQuery(suggestions.get(position), false);
                searchView.clearFocus();
                doSearch(suggestions.get(position));
                return true;
            }
        });

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {

                MyApp.autocompleteService.search(newText, new Callback<Autocomplete>() {
                    @Override
                    public void success(Autocomplete autocomplete, Response response) {
                        suggestions.clear();
                        suggestions.addAll(autocomplete.suggestions);

                        String[] columns = {
                                BaseColumns._ID,
                                SearchManager.SUGGEST_COLUMN_TEXT_1,
                                SearchManager.SUGGEST_COLUMN_INTENT_DATA
                        };

                        MatrixCursor cursor = new MatrixCursor(columns);

                        for (int i = 0; i < autocomplete.suggestions.size(); i++) {
                            String[] tmp = {Integer.toString(i), autocomplete.suggestions.get(i), autocomplete.suggestions.get(i)};
                            cursor.addRow(tmp);
                        }
                        suggestionAdapter.swapCursor(cursor);
                    }

                    @Override
                    public void failure(RetrofitError error) {
                        Toast.makeText(SearchFoodActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
                        Log.w("autocompleteService", error.getMessage());
                    }
                });
                return false;
            }
        });

        return true;
}
Blastopore answered 4/5, 2015 at 14:17 Comment(5)
It would be great if you post the code for your "MyApp.autocompleteService.search"...Scholium
it's a standard Retrofit callBlastopore
Thanks, working. I use okhttp in replace of retrofit.Giselegisella
simple and works! no need any contentprovider or countless other classes. Thanks !Austronesian
This is should be the answer.Alice
C
12

Found the solution on developer.android.com:

If you have suggestions that you get from a network location, you can build a cursor on the fly when you get the results from your server.

This goes inside your ContentProvider's query() method:

String[] columns = {
   BaseColumns._ID, 
   SearchManager.SUGGEST_COLUMN_TEXT_1, 
   SearchManager.SUGGEST_COLUMN_INTENT_DATA
};

MatrixCursor cursor = new MatrixCursor(columns);

for (int i = 0; i < arr.length(); i++)
{
  String[] tmp = {Integer.toString(i), arr.getString(i), arr.getString(i)};
  cursor.addRow(tmp);
}
return cursor;

The cursor is the used in the quick-search box to fill a list of suggestions.

Calabar answered 24/11, 2010 at 12:50 Comment(3)
Where do you perform the network request in the query? I am stuck since its on the UI thread.Ijssel
@Ijssel He already stated that … This goes inside your ContentProvider's query() method: …Juliennejuliet
explain in detail. I don't get it.Sheriff
F
0

Don't know if people still need this. Just in case, for future searchers. I had a problem with my SearchView's suggestions, which did not show up. After searched around, I found a solution for this. I used Volley for my content provider class (to get the suggestions from web api), which seemed not to fit naturally into Android's content provider framework. I also found that my content provider DOES run in the UI thread. Therefore, if I used Volley's Future synchronously in it, it slowed down (blocked) the UI until the request returned (timeout). In addition, I found information around the web that Volley's Future request should be run in other thread, such as in an Async task, for it to work well. Thus, it didn't solve my problem, because if I'd have to use it (in an async task), i would have used Volley's normal (async) request in the first place instead (which was what I used then). What I did was this:

  1. In my ContentProvider subclass, I define a listener interface:

    public interface ResultListener {

      void onProviderResult(Cursor mCursor);
    
      void onProviderError(String errorMsg);
    

    }

  2. In my activity (which implemented LoaderCallbacks), I implemented also above interface.

  3. In my singleton application class, I define a static variable which is used as transient data, together with its getter/setter methods:

    private static HashMap transientData = new HashMap();

    public static Object getTransientData(String objectName) {

    return transientData.get(objectName);
    

    }

    public static void setTransientData(String objectName, Object object) {

    transientData.put(objectName, object);
    

    }

  4. now the logic: In the activity, before calling getSupportLoaderManager().initLoader(...), I called MyApplication.setTransientData("requestor", this) :

In my content provider's class, in the volley request's onResponse callback, I did this:

public void onResponse(JSONArray response){

  ...

  ResultListener requestor =   (ResultListener)TheApplication.getTransientData("requestor");

  if (requestor!=null) requestor.onProviderResult(mCursor);

}

So that when the volley request returned, it will trigger the requestor's callback method, passing it the cursor filled with data from the response, and in turn the requestor (the activity) notified the cursor adapter by calling: adapter.swapCursor(c); adapter.notifyDataSetChanged();

Hope this helps someone. Be blessed.

Fermat answered 30/4, 2016 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.