package edu.mit.mitmobile2; import edu.mit.mitmobile2.MITSearchRecentSuggestions.SuggestionColumns; import android.app.SearchManager; import android.content.ContentValues; import android.content.Context; import android.content.SearchRecentSuggestionsProvider; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.text.TextUtils; import android.util.Log; /** * This is a slightly edited subset of the * android.content.SearchRecentSuggestionsProvider source code. * * The necessity of this class is an unfortunate consequence of the * fact that the built-in suggestions.db is not designed to distinguish * between multiple content authorities within the same app. */ public class MITSearchRecentsProvider extends SearchRecentSuggestionsProvider { /** our own fields **/ private static final String sDatabaseName = "mit_suggestions.db"; private static final String TABLE_NAME = "mit_suggestions"; private static final String TAG = "MITSuggestionsProvider"; /* static final class Columns implements BaseColumns { static final String DISPLAY1 = "display1"; static final String DISPLAY2 = "display2"; static final String QUERY = "query"; static final String DATE = "date"; static final String AUTHORITY = "authority"; } */ /** fields copied from superclass **/ private SQLiteOpenHelper mOpenHelper; private static final String ORDER_BY = "date DESC"; private static final String NULL_COLUMN = SuggestionColumns.QUERY; private static final String sSuggestions = "mit_suggestions"; private Uri mSuggestionsUri; private UriMatcher mUriMatcher; private String mAuthority; private int mMode; private boolean mTwoLineDisplay; private String mSuggestSuggestionClause; private String[] mSuggestionProjection; // Uri and query support private static final int URI_MATCH_SUGGEST = 1; // Table of database versions. Don't forget to update! // NOTE: These version values are shifted left 8 bits (x 256) in order to create space for // a small set of mode bitflags in the version int. // // 1 original implementation with queries, and 1 or 2 display columns // 1->2 added UNIQUE constraint to display1 column private static final int DATABASE_VERSION = 2 * 256; /* @Override String getType(Uri uri) { } */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = mOpenHelper.getReadableDatabase(); // special case for actual suggestions (from search manager) if (mUriMatcher.match(uri) == URI_MATCH_SUGGEST) { String suggestSelection; String[] myArgs; if (TextUtils.isEmpty(selectionArgs[0])) { suggestSelection = SuggestionColumns.AUTHORITY + " LIKE ?"; myArgs = new String[] { mAuthority }; } else { String like = "%" + selectionArgs[0] + "%"; if (mTwoLineDisplay) { myArgs = new String [] { like, like, mAuthority }; } else { myArgs = new String [] { like, mAuthority }; } suggestSelection = mSuggestSuggestionClause; } // Suggestions are always performed with the default sort order Cursor c = db.query(sSuggestions, mSuggestionProjection, suggestSelection, myArgs, null, null, ORDER_BY, null); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } return super.query(uri, projection, selection, selectionArgs, sortOrder); } /** * code copied from superclass and amended to match our table columns */ @Override protected void setupSuggestions(String authority, int mode) { if (TextUtils.isEmpty(authority) || ((mode & DATABASE_MODE_QUERIES) == 0)) { throw new IllegalArgumentException(); } // unpack mode flags mTwoLineDisplay = (0 != (mode & DATABASE_MODE_2LINES)); // saved values mAuthority = new String(authority); mMode = mode; // derived values mSuggestionsUri = Uri.parse("content://" + mAuthority + "/" + TABLE_NAME); mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); mUriMatcher.addURI(mAuthority, SearchManager.SUGGEST_URI_PATH_QUERY, URI_MATCH_SUGGEST); if (mTwoLineDisplay) { mSuggestSuggestionClause = "(" + SuggestionColumns.DISPLAY1 + " LIKE ? OR " + SuggestionColumns.DISPLAY2 + " LIKE ?) AND " + SuggestionColumns.AUTHORITY + " LIKE ?"; mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, SuggestionColumns.DISPLAY1 + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, SuggestionColumns.DISPLAY2 + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, SuggestionColumns.QUERY + " AS " + SearchManager.SUGGEST_COLUMN_QUERY, SuggestionColumns._ID }; } else { mSuggestSuggestionClause = SuggestionColumns.DISPLAY1 + " LIKE ? AND " + SuggestionColumns.AUTHORITY + " LIKE ?"; mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, SuggestionColumns.DISPLAY1 + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, SuggestionColumns.QUERY + " AS " + SearchManager.SUGGEST_COLUMN_QUERY, SuggestionColumns._ID }; } } private static class DatabaseHelper extends SQLiteOpenHelper { private int mNewVersion; public DatabaseHelper(Context context, int newVersion) { super(context, sDatabaseName, null, newVersion); mNewVersion = newVersion; } @Override public void onCreate(SQLiteDatabase db) { StringBuilder builder = new StringBuilder(); builder.append("CREATE TABLE " + TABLE_NAME + " ("); builder.append(SuggestionColumns._ID + " INTEGER PRIMARY KEY,"); builder.append(SuggestionColumns.DISPLAY1 + " TEXT UNIQUE ON CONFLICT REPLACE, "); if (0 != (mNewVersion & DATABASE_MODE_2LINES)) { builder.append(SuggestionColumns.DISPLAY2 + " TEXT,"); } builder.append(SuggestionColumns.QUERY + " TEXT, "); builder.append(SuggestionColumns.AUTHORITY + " TEXT, "); builder.append(SuggestionColumns.DATE + " LONG);"); db.execSQL(builder.toString()); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } } /** * code copied verbatim from superclass */ @Override public boolean onCreate() { if (mAuthority == null || mMode == 0) { throw new IllegalArgumentException("Provider not configured"); } int mWorkingDbVersion = DATABASE_VERSION + mMode; mOpenHelper = new DatabaseHelper(getContext(), mWorkingDbVersion); return true; } /** * This method is provided for use by the ContentResolver. Do not override, or directly * call from your own code. */ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int length = uri.getPathSegments().size(); if (length < 1) { throw new IllegalArgumentException("Unknown Uri"); } // Note: This table has on-conflict-replace semantics, so insert() may actually replace() long rowID = -1; String base = uri.getPathSegments().get(0); Uri newUri = null; if (base.equals(sSuggestions)) { if (length == 1) { rowID = db.insert(sSuggestions, NULL_COLUMN, values); if (rowID > 0) { newUri = Uri.withAppendedPath(mSuggestionsUri, String.valueOf(rowID)); } } } if (rowID < 0) { throw new IllegalArgumentException("Unknown Uri"); } getContext().getContentResolver().notifyChange(newUri, null); return newUri; } /** * code copied verbatim from superclass */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final int length = uri.getPathSegments().size(); if (length != 1) { throw new IllegalArgumentException("Unknown Uri"); } final String base = uri.getPathSegments().get(0); int count = 0; if (base.equals(sSuggestions)) { count = db.delete(sSuggestions, selection, selectionArgs); } else { throw new IllegalArgumentException("Unknown Uri"); } getContext().getContentResolver().notifyChange(uri, null); return count; } }