Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does ActionBarSherlock 4.2 support search suggestions for a SearchView?

A month ago, I dropped-in ActionBarSherlock 4.2 into my project. I got everything to work, except the search suggestions for my SearchView. The way I was creating search suggestions was using the method in the Android documentation.

Does ActionBarSherlock support search suggestions? I tried to dig through the issue list on the Github page but the issue seems closed but I can't seem to follow the discussion and understand whether it really is a resolved or not. I thought that some of you who've been using ActionBarSherlock might know better.

like image 907
Mridang Agarwalla Avatar asked Dec 10 '12 10:12

Mridang Agarwalla


2 Answers

It doesn't. But I have found a way to make it query your ContentProvider. I looked into the source of SuggestionsAdapter from API 17 where the query executes and got an idea of replacing this method. Also I found that ActionbarSherlock's SuggestionsAdapter does not use your SearchableInfo.

Edit com.actionbarsherlock.widget.SuggestionsAdapter in your ActionBarSherlock project:

Add a line

private SearchableInfo searchable;

in constructor, add

this.searchable = mSearchable;

Replace getSuggestions method with this one:

public Cursor getSuggestions(String query, int limit) {

    if (searchable == null) {
        return null;
    }

    String authority = searchable.getSuggestAuthority();
    if (authority == null) {
        return null;
    }

    Uri.Builder uriBuilder = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(authority)
            .query("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
            .fragment("");  // TODO: Remove, workaround for a bug in Uri.writeToParcel()

    // if content path provided, insert it now
    final String contentPath = searchable.getSuggestPath();
    if (contentPath != null) {
        uriBuilder.appendEncodedPath(contentPath);
    }

    // append standard suggestion query path
    uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);

    // get the query selection, may be null
    String selection = searchable.getSuggestSelection();
    // inject query, either as selection args or inline
    String[] selArgs = null;
    if (selection != null) {    // use selection if provided
        selArgs = new String[] { query };
    } else {                    // no selection, use REST pattern
        uriBuilder.appendPath(query);
    }

    if (limit > 0) {
        uriBuilder.appendQueryParameter("limit", String.valueOf(limit));
    }

    Uri uri = uriBuilder.build();

    // finally, make the query
    return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
}

Now it queries my ContentProvider but crashes with default adapter saying that no layout_height loading some xml file from support library. So you have to use custom SuggestionsAdapter. This is what worked for me:

import com.actionbarsherlock.widget.SearchView;

import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public final class DrugsSearchAdapter extends CursorAdapter
{
    private static final int QUERY_LIMIT = 50;

    private LayoutInflater inflater;
    private SearchView searchView;
    private SearchableInfo searchable;

    public DrugsSearchAdapter(Context context, SearchableInfo info, SearchView searchView)
    {
        super(context, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        this.searchable = info;
        this.searchView = searchView;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public void bindView(View v, Context context, Cursor c)
    {
        String name = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
        TextView namet = (TextView) v.findViewById(R.id.list_item_drug_name);
        namet.setText(name);

        String man = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2));
        TextView manuf = (TextView) v.findViewById(R.id.list_item_drug_manufacturer);
        manuf.setText(man);
    }

    @Override
    public View newView(Context arg0, Cursor arg1, ViewGroup arg2)
    {
        return this.inflater.inflate(R.layout.list_item_drug_search, null);
    }

    /**
     * Use the search suggestions provider to obtain a live cursor.  This will be called
     * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
     * The results will be processed in the UI thread and changeCursor() will be called.
     */
    @Override
    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
        String query = (constraint == null) ? "" : constraint.toString();
        /**
         * for in app search we show the progress spinner until the cursor is returned with
         * the results.
         */
        Cursor cursor = null;
        if (searchView.getVisibility() != View.VISIBLE
                || searchView.getWindowVisibility() != View.VISIBLE) {
            return null;
        }
        try {
            cursor = getSuggestions(searchable, query, QUERY_LIMIT);
            // trigger fill window so the spinner stays up until the results are copied over and
            // closer to being ready
            if (cursor != null) {
                cursor.getCount();
                return cursor;
            }
        } catch (RuntimeException e) {
        }
        // If cursor is null or an exception was thrown, stop the spinner and return null.
        // changeCursor doesn't get called if cursor is null
        return null;
    }

    public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {

        if (searchable == null) {
            return null;
        }

        String authority = searchable.getSuggestAuthority();
        if (authority == null) {
            return null;
        }

        Uri.Builder uriBuilder = new Uri.Builder()
                .scheme(ContentResolver.SCHEME_CONTENT)
                .authority(authority)
                .query("") 
                .fragment(""); 

        // if content path provided, insert it now
        final String contentPath = searchable.getSuggestPath();
        if (contentPath != null) {
            uriBuilder.appendEncodedPath(contentPath);
        }

        // append standard suggestion query path
        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);

        // get the query selection, may be null
        String selection = searchable.getSuggestSelection();
        // inject query, either as selection args or inline
        String[] selArgs = null;
        if (selection != null) {    // use selection if provided
            selArgs = new String[] { query };
        } else {                    // no selection, use REST pattern
            uriBuilder.appendPath(query);
        }

        if (limit > 0) {
            uriBuilder.appendQueryParameter("limit", String.valueOf(limit));
        }

        Uri uri = uriBuilder.build();

        // finally, make the query
        return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
    }

}

And set this adapter in SearchView

searchView.setSuggestionsAdapter(new DrugsSearchAdapter(this, searchManager.getSearchableInfo(getComponentName()), searchView));
like image 102
Yaroslav Mytkalyk Avatar answered Oct 03 '22 16:10

Yaroslav Mytkalyk


I'm the one that opened the github issue for this. It is working on the dev branch. The current version (4.2) doesn't have the fix. It was completely fixed by this commit, but I would suggest just checking out the dev branch and trying it.

like image 35
yincrash Avatar answered Oct 03 '22 15:10

yincrash