1

I want to return search suggestions for the ActionBar SearchView from a ContentProvider that asynchronously loads these suggestions from a webservice using Volley. However, I am struggling to find a way to notify the dropdown of changes to the MatrixCursor. As of now, the suggestions retrieved from the webservice are only visible when typing another character, because this is when the previously updated cursor is returned. Is there an elegant way to either notify the DropdownView or force a requery after the initial query has been completed?

SearchSuggestionProvider:

@Override
@DebugLog
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    switch (uriMatcher.match(uri)) {
        case SEARCH_SUGGEST:
            String searchQuery = uri.getLastPathSegment().toLowerCase();
            Log.d(TAG, "searchQuery: " + searchQuery);
            if (searchQuery.equals("search_suggest_query") || searchQuery.length() < 3) {
                return mAsyncCursor;
            }
            if (mRequest != null && !mRequest.hasHadResponseDelivered()) {
                mRequest.cancel();
            }
            String url = API.buildUri(API.PRODUCT, searchQuery, SEARCH_QUERY_LIMIT, SEARCH_QUERY_MIN, 0);
            mRequest = new GsonRequest<>(url, Product[].class, null, new Response.Listener<Product[]>() {
                @Override
                @DebugLog
                public void onResponse(Product[] products) {
                    onAsyncResponse(products);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                    // nothing
                }
            });
            AppController.getInstance().addToRequestQueue(mRequest, TAG);
            // return an empty cursor as long as no async response is returned
            return mAsyncCursor;
        default:
            throw new IllegalArgumentException(uri.toString());
    }
}    

/**
 * Updates the current cursor with a new cursor when we get a response
 * @param products
 */
@DebugLog
private void onAsyncResponse(Product[] products) {
    MatrixCursor newCursor = createCursor();
    for (int i = 0; i < products.length; i++) {
        Product product = products[i];
        mAsyncCursor.addRow(new String[] {
                String.valueOf(product.getId()), // _ID
                null, // todo SUGGEST_COLUMN_ICON_1
                product.getName(), // SUGGEST_COLUMN_TEXT_1
                null, // todo SUGGEST_COLUMN_TEXT_2
                String.valueOf(product.getId()) // SUGGEST_COLUMN_INTENT_DATA_ID
        });
    }
    mAsyncCursor = newCursor;
}

private MatrixCursor createCursor() {
    return new MatrixCursor(SEARCH_SUGGEST_COLUMNS, SEARCH_QUERY_LIMIT);
}
pnuts
  • 58,317
  • 11
  • 87
  • 139
thyrel
  • 453
  • 1
  • 4
  • 13

2 Answers2

0

You don't need to make a asynchronous HTTP call in query method since the query method of the content provider is NOT on the main thread. So the solution for you is to make the synchronous HTTP call using Volley directly in the query method, then you can achieve your goal.

Also check here if you still have any issues: Create asynchronous ContentProvider for Actionbar SearchView

Community
  • 1
  • 1
Charlie MAI
  • 93
  • 11
0

Don't know if people still need this. Just in case, for future searchers, I found a solution for this. I also used Volley for my content provider class, which seemed not to fit naturally into Android's content provider framework. As opposed to muetzenflo's answer, I found that my content provider DOES run in the UI thread. Therefore, when I used Volley's Future synchronously in it, it slowed down (blocked) the UI until the request returned (timeout). In addition to this, 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<String, Object> transientData = new HashMap<String, Object>();
    
    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.

olibiaz
  • 2,551
  • 4
  • 29
  • 31
fiveloaves
  • 26
  • 4
  • Please don't add the same answer to multiple questions. Answer the best one and flag the rest as duplicates. See [Is it acceptable to add a duplicate answer to several questions?](http://meta.stackexchange.com/q/104227) – Bhargav Rao Apr 30 '16 at 19:49
  • Hi, Bhargav. Sorry for the inconvience, I am new here. just tried to help. – fiveloaves May 01 '16 at 17:15