Search code examples
androidsqliteandroid-contentproviderandroid-searchmanagersearch-suggestion

Android Custom Search Suggestions: Query Two Parameters


SQLite Structure

Table: Class

 Column1: classID (primary key)
 Column2: className


Table: Students

 Column1: studentID (primary key)
 Column2: classID (foreign key)
 Column3: studentName

Question

I have followed the Adding Custom Suggestions developer guide. The system correctly calls my content provider's query function when I type in the action bar search widget, and I can see search suggestions. My problem is that the query returns suggestions from the entire students table (which is the expected behaviour). But I need to limit the suggestions for a given classID. But the way the search suggestions work as described in the developer guide, only one parameter is passed to the query function, which in my case is selectionArgs[0].

searchable.xml

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:hint="@string/student_search_hint"
    android:label="@string/app_name"
    android:searchSuggestThreshold="2"
    android:searchSuggestAuthority="com.androidapp.providers.database"
    android:searchSuggestSelection="studentName LIKE ? "
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:searchMode="queryRewriteFromText" >

excerpt from content provider

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

    ...

    queryBuilder.setTables(StudentsTable.TABLE);

    HashMap<String, String> columnMap = new HashMap<String, String>();
    columnMap.put(BaseColumns._ID, StudentsTable.STUDENT_ID + " AS " + BaseColumns._ID);
    columnMap.put(SearchManager.SUGGEST_COLUMN_TEXT_1, StudentsTable.NAME + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
    columnMap.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, StudentsTable.STUDENT_ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA);
    queryBuilder.setProjectionMap(columnMap);

    limit = uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);
    selectionArgs[0] = "%"+selectionArgs[0] + "%";

    SQLiteDatabase db = database.getWritableDatabase();
    Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit);

    return cursor;
}

If the query was initiated by me I could simply pass the second parameter (classID) in selectionArgs[1] and modify the selection string appropriately to form the correct SQLite statement.

But the query is initiated by the system. So how do I do this? Is there away to intercept the call to my content provider and modify the call to query?


Solution

  • I hope this is helpful to someone, I spent days trying to figure this out, Thanks to the link provided by pskink I was able to implement the following solution:

    I created a variable in the content provider and an appropriate setter method:

    public class MyContentProvider extends ContentProvider {
    
        ...
    
        private long id;
    
        ...
    
        public void setId(long id) {
            this.id = id;     
        }
    
    }
    

    Then in my activity where I want to provide search suggestions for students in a particular class only, I do:

    ContentProviderClient client = getContentResolver().acquireContentProviderClient("com.androidapp.providers.database");
    MyContentProvider provider = (MyContentProvider)client.getLocalContentProvider();
    provider.setId(rowid); //rowid is the classId from which students will be searched
    client.release();
    

    Ammend the query function:

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,String[], selectionArgs, String sortOrder) {
    
        ...
    
        queryBuilder.setTables(StudentsTable.TABLE);
    
        HashMap<String, String> columnMap = new HashMap<String, String>();
        columnMap.put(BaseColumns._ID, StudentsTable.STUDENT_ID + " AS " + BaseColumns._ID);
        columnMap.put(SearchManager.SUGGEST_COLUMN_TEXT_1, StudentsTable.NAME + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
        columnMap.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, StudentsTable.STUDENT_ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA);
        queryBuilder.setProjectionMap(columnMap);
    
        limit = uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);
        selectionArgs[0] = "%" + selectionArgs[0] + "%";
    
        /*AMMENDMENT*/
        selection = selection + "AND " + StudentsTable.CLASS_ID + "=" + id;
        //This forms the SQL statement "WHERE studentName LIKE ? AND classId=3" (Assuming that rowid was 3).
        //As per normal Android behaviour the "?" mark will be replaced by selectionArgs[0].
        /*END*/
    
        SQLiteDatabase db = database.getWritableDatabase();
        Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit);
    
        return cursor;
    }