| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.searchabledict; |
| |
| import android.app.SearchManager; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteOpenHelper; |
| import android.database.sqlite.SQLiteQueryBuilder; |
| import android.provider.BaseColumns; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.HashMap; |
| |
| /** |
| * Contains logic to return specific words from the dictionary, and |
| * load the dictionary table when it needs to be created. |
| */ |
| public class DictionaryDatabase { |
| private static final String TAG = "DictionaryDatabase"; |
| |
| //The columns we'll include in the dictionary table |
| public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1; |
| public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2; |
| |
| private static final String DATABASE_NAME = "dictionary"; |
| private static final String FTS_VIRTUAL_TABLE = "FTSdictionary"; |
| private static final int DATABASE_VERSION = 2; |
| |
| private final DictionaryOpenHelper mDatabaseOpenHelper; |
| private static final HashMap<String,String> mColumnMap = buildColumnMap(); |
| |
| /** |
| * Constructor |
| * @param context The Context within which to work, used to create the DB |
| */ |
| public DictionaryDatabase(Context context) { |
| mDatabaseOpenHelper = new DictionaryOpenHelper(context); |
| } |
| |
| /** |
| * Builds a map for all columns that may be requested, which will be given to the |
| * SQLiteQueryBuilder. This is a good way to define aliases for column names, but must include |
| * all columns, even if the value is the key. This allows the ContentProvider to request |
| * columns w/o the need to know real column names and create the alias itself. |
| */ |
| private static HashMap<String,String> buildColumnMap() { |
| HashMap<String,String> map = new HashMap<String,String>(); |
| map.put(KEY_WORD, KEY_WORD); |
| map.put(KEY_DEFINITION, KEY_DEFINITION); |
| map.put(BaseColumns._ID, "rowid AS " + |
| BaseColumns._ID); |
| map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + |
| SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); |
| map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " + |
| SearchManager.SUGGEST_COLUMN_SHORTCUT_ID); |
| return map; |
| } |
| |
| /** |
| * Returns a Cursor positioned at the word specified by rowId |
| * |
| * @param rowId id of word to retrieve |
| * @param columns The columns to include, if null then all are included |
| * @return Cursor positioned to matching word, or null if not found. |
| */ |
| public Cursor getWord(String rowId, String[] columns) { |
| String selection = "rowid = ?"; |
| String[] selectionArgs = new String[] {rowId}; |
| |
| return query(selection, selectionArgs, columns); |
| |
| /* This builds a query that looks like: |
| * SELECT <columns> FROM <table> WHERE rowid = <rowId> |
| */ |
| } |
| |
| /** |
| * Returns a Cursor over all words that match the given query |
| * |
| * @param query The string to search for |
| * @param columns The columns to include, if null then all are included |
| * @return Cursor over all words that match, or null if none found. |
| */ |
| public Cursor getWordMatches(String query, String[] columns) { |
| String selection = KEY_WORD + " MATCH ?"; |
| String[] selectionArgs = new String[] {query+"*"}; |
| |
| return query(selection, selectionArgs, columns); |
| |
| /* This builds a query that looks like: |
| * SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*' |
| * which is an FTS3 search for the query text (plus a wildcard) inside the word column. |
| * |
| * - "rowid" is the unique id for all rows but we need this value for the "_id" column in |
| * order for the Adapters to work, so the columns need to make "_id" an alias for "rowid" |
| * - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order |
| * for suggestions to carry the proper intent data. |
| * These aliases are defined in the DictionaryProvider when queries are made. |
| * - This can be revised to also search the definition text with FTS3 by changing |
| * the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across |
| * the entire table, but sorting the relevance could be difficult. |
| */ |
| } |
| |
| /** |
| * Performs a database query. |
| * @param selection The selection clause |
| * @param selectionArgs Selection arguments for "?" components in the selection |
| * @param columns The columns to return |
| * @return A Cursor over all rows matching the query |
| */ |
| private Cursor query(String selection, String[] selectionArgs, String[] columns) { |
| /* The SQLiteBuilder provides a map for all possible columns requested to |
| * actual columns in the database, creating a simple column alias mechanism |
| * by which the ContentProvider does not need to know the real column names |
| */ |
| SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); |
| builder.setTables(FTS_VIRTUAL_TABLE); |
| builder.setProjectionMap(mColumnMap); |
| |
| Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(), |
| columns, selection, selectionArgs, null, null, null); |
| |
| if (cursor == null) { |
| return null; |
| } else if (!cursor.moveToFirst()) { |
| cursor.close(); |
| return null; |
| } |
| return cursor; |
| } |
| |
| |
| /** |
| * This creates/opens the database. |
| */ |
| private static class DictionaryOpenHelper extends SQLiteOpenHelper { |
| |
| private final Context mHelperContext; |
| private SQLiteDatabase mDatabase; |
| |
| /* Note that FTS3 does not support column constraints and thus, you cannot |
| * declare a primary key. However, "rowid" is automatically used as a unique |
| * identifier, so when making requests, we will use "_id" as an alias for "rowid" |
| */ |
| private static final String FTS_TABLE_CREATE = |
| "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE + |
| " USING fts3 (" + |
| KEY_WORD + ", " + |
| KEY_DEFINITION + ");"; |
| |
| DictionaryOpenHelper(Context context) { |
| super(context, DATABASE_NAME, null, DATABASE_VERSION); |
| mHelperContext = context; |
| } |
| |
| @Override |
| public void onCreate(SQLiteDatabase db) { |
| mDatabase = db; |
| mDatabase.execSQL(FTS_TABLE_CREATE); |
| loadDictionary(); |
| } |
| |
| /** |
| * Starts a thread to load the database table with words |
| */ |
| private void loadDictionary() { |
| new Thread(new Runnable() { |
| public void run() { |
| try { |
| loadWords(); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }).start(); |
| } |
| |
| private void loadWords() throws IOException { |
| Log.d(TAG, "Loading words..."); |
| final Resources resources = mHelperContext.getResources(); |
| InputStream inputStream = resources.openRawResource(R.raw.definitions); |
| BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); |
| |
| try { |
| String line; |
| while ((line = reader.readLine()) != null) { |
| String[] strings = TextUtils.split(line, "-"); |
| if (strings.length < 2) continue; |
| long id = addWord(strings[0].trim(), strings[1].trim()); |
| if (id < 0) { |
| Log.e(TAG, "unable to add word: " + strings[0].trim()); |
| } |
| } |
| } finally { |
| reader.close(); |
| } |
| Log.d(TAG, "DONE loading words."); |
| } |
| |
| /** |
| * Add a word to the dictionary. |
| * @return rowId or -1 if failed |
| */ |
| public long addWord(String word, String definition) { |
| ContentValues initialValues = new ContentValues(); |
| initialValues.put(KEY_WORD, word); |
| initialValues.put(KEY_DEFINITION, definition); |
| |
| return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues); |
| } |
| |
| @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 " + FTS_VIRTUAL_TABLE); |
| onCreate(db); |
| } |
| } |
| |
| } |