am beca3af7: cherry-pick from master: 3c26ae869e
Merge commit 'beca3af74d2a5baeba00ada49ba2480d36535f5e' into froyo-plus-aosp
* commit 'beca3af74d2a5baeba00ada49ba2480d36535f5e':
cherry-pick from master: 3c26ae869e
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
** Copyright 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.
|
||||
@@ -24,9 +24,8 @@
|
||||
<application android:label="@string/app_name"
|
||||
android:icon="@drawable/ic_dictionary">
|
||||
|
||||
<!-- The default activity of the app. Can also display search results. -->
|
||||
<!-- The default activity of the app; displays search results. -->
|
||||
<activity android:name=".SearchableDictionary"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.NoTitleBar">
|
||||
|
||||
<intent-filter>
|
||||
@@ -37,22 +36,26 @@
|
||||
<!-- Receives the search request. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<!-- No category needed, because the Intent will specify this class component-->
|
||||
</intent-filter>
|
||||
|
||||
<!-- Points to searchable meta data. -->
|
||||
<meta-data android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable"/>
|
||||
android:resource="@xml/searchable" />
|
||||
|
||||
</activity>
|
||||
|
||||
<!-- Displays the definition of a word. -->
|
||||
<activity android:name=".WordActivity"
|
||||
android:theme="@android:style/Theme.NoTitleBar"/>
|
||||
android:theme="@android:style/Theme.NoTitleBar" />
|
||||
|
||||
<!-- Provides search suggestions for words and their definitions. -->
|
||||
<provider android:name="DictionaryProvider"
|
||||
android:authorities="dictionary"
|
||||
android:syncable="false" />
|
||||
<provider android:name=".DictionaryProvider"
|
||||
android:authorities="com.example.android.searchabledict.DictionaryProvider" />
|
||||
|
||||
<!-- Points to searchable activity so the whole app can invoke search. -->
|
||||
<meta-data android:name="android.app.default_searchable"
|
||||
android:value=".SearchableDictionary" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<p>A sample application that demonstrates Android's search framework.</p>
|
||||
|
||||
<p>This application includes a dictionary of words. By invoking a search inside the app
|
||||
(via the device search button or Menu > Search), a local search will be performed
|
||||
<p>This application includes a dictionary of words. By invoking the Android search dialog inside the
|
||||
app (via the device search button or Menu > Search), you can perform a search
|
||||
across the dictionary. As you type, suggestions will appear, which you can select
|
||||
to view the full definition. You can also execute the search to view all word definitions
|
||||
to view the complete definition. You can also execute the search to view all word definitions
|
||||
that match the entered text.</p>
|
||||
|
||||
<p>The application also grants content provider privileges to
|
||||
@@ -11,11 +11,22 @@ Quick Search Box, Android's system-wide search tool. This means that the
|
||||
dictionary definitions can be offered as search suggestions outside of the application,
|
||||
when text is entered into Quick Search Box.</p>
|
||||
|
||||
<p>The code in this application demonstrates how to:</p>
|
||||
<ul>
|
||||
<li>Implement a search interface using Android's search framework</li>
|
||||
<li>Provide custom search suggestions and offer them in Quick Search Box</li>
|
||||
<li>Create an FTS3 table in SQLite and perform full-text search</li>
|
||||
<li>Create a content provider to perform all searches and queries to the dictionary</li>
|
||||
<li>Use <a
|
||||
href="../../../reference/android/widget/SimpleCursorAdapter.html">SimpleCursorAdapter</a> to bind
|
||||
data from a Cursor to a ListView.</li>
|
||||
</ul>
|
||||
|
||||
<p>See also:</p>
|
||||
<ul>
|
||||
<li><a href="../../../reference/android/app/SearchManager.html">SearchManager</a></li>
|
||||
<li><a href="../../../reference/android/content/ContentProvider.html">ContentProvider</a></li>
|
||||
<li><a href="../../../guide/topics/search/index.html">Search Developer Guide</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<img src="../images/SearchableDictionary1.png" />
|
||||
<img src="../images/SearchableDictionary2.png" />
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
** Copyright 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.
|
||||
@@ -16,22 +16,25 @@
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<!-- Layout for SearchableActivity.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:id="@+id/textField"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search_instructions"/>
|
||||
|
||||
android:id="@+id/text"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="17dp"
|
||||
android:text="@string/search_instructions"
|
||||
android:background="@android:drawable/title_bar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<ListView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
37
samples/SearchableDictionary/res/layout/result.xml
Normal file
37
samples/SearchableDictionary/res/layout/result.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 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.
|
||||
*/
|
||||
-->
|
||||
<!-- Layout for list items in the search results.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="5dp">
|
||||
<TextView
|
||||
android:id="@+id/word"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/definition"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
android:singleLine="true"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
** Copyright 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.
|
||||
@@ -16,24 +16,24 @@
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<!-- Layout for WordActivity.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="5dp">
|
||||
<TextView
|
||||
android:id="@+id/word"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="30sp"
|
||||
android:textSize="35dp"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search_instructions"
|
||||
android:layout_marginLeft="10dip"
|
||||
android:layout_marginTop="5dip" />
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/definition"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search_instructions"
|
||||
android:layout_marginLeft="10dip" />
|
||||
android:textSize="18dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:paddingTop="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
24
samples/SearchableDictionary/res/menu/options_menu.xml
Normal file
24
samples/SearchableDictionary/res/menu/options_menu.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
/*
|
||||
** Copyright 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.
|
||||
*/
|
||||
-->
|
||||
<!-- Options Menu for SearchableActivity and WordActivity.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/search"
|
||||
android:title="@string/menu_search"
|
||||
android:icon="@drawable/ic_menu_search" />
|
||||
</menu>
|
||||
@@ -56,6 +56,7 @@ analogy - n. drawing a comparison in order to show a similarity in some respect
|
||||
analyst - n. someone who is skilled at analyzing data
|
||||
analyze - v. break down into components or essential features
|
||||
anchor - v. fix firmly and stably
|
||||
android - n. a robot designed to look and behave like a human being
|
||||
annual - j. occurring or payable every year
|
||||
anonymous - j. having no known name or identity or known source
|
||||
antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
** Copyright 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.
|
||||
@@ -16,14 +16,17 @@
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<resources>
|
||||
|
||||
<!-- The name of the application. -->
|
||||
<string name="app_name">Searchable Dictionary</string>
|
||||
|
||||
<!-- The label for the search results for this app (see searchable.xml for more details). -->
|
||||
<!-- The label for use as a searchable item -->
|
||||
<string name="search_label">Dictionary</string>
|
||||
|
||||
<!-- The hint text that appears in the search box. -->
|
||||
<string name="search_hint">Search the dictionary</string>
|
||||
|
||||
<!-- The menu entry that invokes search. -->
|
||||
<string name="menu_search">Search</string>
|
||||
|
||||
@@ -34,5 +37,12 @@
|
||||
<string name="search_instructions">Press the search key to look up a word</string>
|
||||
|
||||
<!-- Shown above search results when we receive a search request. -->
|
||||
<string name="search_results">Search results for \'<xliff:g id="string">%s</xliff:g>\': </string>
|
||||
<plurals name="search_results">
|
||||
<item quantity="one">%d result for \"%s\": </item>
|
||||
<item quantity="other">%d results for \"%s\": </item>
|
||||
</plurals>
|
||||
|
||||
<!-- Search failure message. -->
|
||||
<string name="no_results">No results found for \"%s\"</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
** Copyright 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.
|
||||
@@ -17,24 +17,20 @@
|
||||
*/
|
||||
-->
|
||||
|
||||
<!-- The attributes below configure how the search results will work:
|
||||
- the 'label' points to a short description used when searching within the application if
|
||||
'badge mode' is used as specified with android:searchMode="useLabelAsBadge" (which it is not for
|
||||
this application).
|
||||
- the 'searchSettingsDescription' points to a string that will be displayed underneath the
|
||||
name of this application in the search settings to describe what content will be searched.
|
||||
- 'includeInGlobalSearch' will include this app's search suggestions in Quick Search Box.
|
||||
- 'searchSuggestAuthority' specifies the authority matching the authority of the
|
||||
"DictionaryProvider" specified in the manifest. This means the DictionaryProvider will be
|
||||
queried for search suggestions.
|
||||
- 'searchSuggestIntentAction' the default intent action used in the intent that is launched based
|
||||
on a user cilcking on a search suggestion. This saves us from manually having to fill in the
|
||||
SUGGEST_COLUMN_INTENT_ACTION column for each suggestion returned by the provider.
|
||||
<!-- The attributes below configure the Android search box appearance
|
||||
and the search suggestions settings.
|
||||
See the Developer Guide for more information
|
||||
http://developer.android.com/guide/topics/search/
|
||||
-->
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="@string/search_label"
|
||||
android:hint="@string/search_hint"
|
||||
android:searchSettingsDescription="@string/settings_description"
|
||||
android:searchSuggestAuthority="com.example.android.searchabledict.DictionaryProvider"
|
||||
android:searchSuggestIntentAction="android.intent.action.VIEW"
|
||||
android:searchSuggestIntentData="content://com.example.android.searchabledict.DictionaryProvider/dictionary"
|
||||
android:searchSuggestSelection=" ?"
|
||||
android:searchSuggestThreshold="1"
|
||||
android:includeInGlobalSearch="true"
|
||||
android:searchSuggestAuthority="dictionary"
|
||||
android:searchSuggestIntentAction="android.intent.action.VIEW">
|
||||
</searchable>
|
||||
>
|
||||
</searchable>
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.content.res.Resources;
|
||||
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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Contains logic to load the word of words and definitions and find a list of matching words
|
||||
* given a query. Everything is held in memory; this is not a robust way to serve lots of
|
||||
* words and is only for demo purposes.
|
||||
*
|
||||
* You may want to consider using an SQLite database. In practice, you'll want to make sure your
|
||||
* suggestion provider is as efficient as possible, as the system will be taxed while performing
|
||||
* searches across many sources for each keystroke the user enters into Quick Search Box.
|
||||
*/
|
||||
public class Dictionary {
|
||||
|
||||
public static class Word {
|
||||
public final String word;
|
||||
public final String definition;
|
||||
|
||||
public Word(String word, String definition) {
|
||||
this.word = word;
|
||||
this.definition = definition;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Dictionary sInstance = new Dictionary();
|
||||
|
||||
public static Dictionary getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private final Map<String, List<Word>> mDict = new ConcurrentHashMap<String, List<Word>>();
|
||||
|
||||
private Dictionary() {
|
||||
}
|
||||
|
||||
private boolean mLoaded = false;
|
||||
|
||||
/**
|
||||
* Loads the words and definitions if they haven't been loaded already.
|
||||
*
|
||||
* @param resources Used to load the file containing the words and definitions.
|
||||
*/
|
||||
public synchronized void ensureLoaded(final Resources resources) {
|
||||
if (mLoaded) return;
|
||||
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
loadWords(resources);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private synchronized void loadWords(Resources resources) throws IOException {
|
||||
if (mLoaded) return;
|
||||
|
||||
Log.d("dict", "loading words");
|
||||
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;
|
||||
addWord(strings[0].trim(), strings[1].trim());
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
mLoaded = true;
|
||||
}
|
||||
|
||||
|
||||
public List<Word> getMatches(String query) {
|
||||
List<Word> list = mDict.get(query);
|
||||
return list == null ? Collections.EMPTY_LIST : list;
|
||||
}
|
||||
|
||||
private void addWord(String word, String definition) {
|
||||
final Word theWord = new Word(word, definition);
|
||||
|
||||
final int len = word.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
final String prefix = word.substring(0, len - i);
|
||||
addMatch(prefix, theWord);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMatch(String query, Word word) {
|
||||
List<Word> matches = mDict.get(query);
|
||||
if (matches == null) {
|
||||
matches = new ArrayList<Word>();
|
||||
mDict.put(query, matches);
|
||||
}
|
||||
matches.add(word);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -18,144 +18,186 @@ package com.example.android.searchabledict;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.List;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Provides search suggestions for a list of words and their definitions.
|
||||
* Provides access to the dictionary database.
|
||||
*/
|
||||
public class DictionaryProvider extends ContentProvider {
|
||||
String TAG = "DictionaryProvider";
|
||||
|
||||
public static String AUTHORITY = "dictionary";
|
||||
public static String AUTHORITY = "com.example.android.searchabledict.DictionaryProvider";
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/dictionary");
|
||||
|
||||
private static final int SEARCH_SUGGEST = 0;
|
||||
private static final int SHORTCUT_REFRESH = 1;
|
||||
// MIME types used for searching words or looking up a single definition
|
||||
public static final String WORDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
|
||||
"/vnd.example.android.searchabledict";
|
||||
public static final String DEFINITION_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +
|
||||
"/vnd.example.android.searchabledict";
|
||||
|
||||
private DictionaryDatabase mDictionary;
|
||||
|
||||
// UriMatcher stuff
|
||||
private static final int SEARCH_WORDS = 0;
|
||||
private static final int GET_WORD = 1;
|
||||
private static final int SEARCH_SUGGEST = 2;
|
||||
private static final int REFRESH_SHORTCUT = 3;
|
||||
private static final UriMatcher sURIMatcher = buildUriMatcher();
|
||||
|
||||
/**
|
||||
* The columns we'll include in our search suggestions. There are others that could be used
|
||||
* to further customize the suggestions, see the docs in {@link SearchManager} for the details
|
||||
* on additional columns that are supported.
|
||||
*/
|
||||
private static final String[] COLUMNS = {
|
||||
"_id", // must include this column
|
||||
SearchManager.SUGGEST_COLUMN_TEXT_1,
|
||||
SearchManager.SUGGEST_COLUMN_TEXT_2,
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up a uri matcher for search suggestion and shortcut refresh queries.
|
||||
* Builds up a UriMatcher for search suggestion and shortcut refresh queries.
|
||||
*/
|
||||
private static UriMatcher buildUriMatcher() {
|
||||
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
// to get definitions...
|
||||
matcher.addURI(AUTHORITY, "dictionary", SEARCH_WORDS);
|
||||
matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
|
||||
// to get suggestions...
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, SHORTCUT_REFRESH);
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SHORTCUT_REFRESH);
|
||||
|
||||
/* The following are unused in this implementation, but if we include
|
||||
* {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
|
||||
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
|
||||
* Quick Search Box, in which case, the following Uris would be provided and we
|
||||
* would return a cursor with a single item representing the refreshed suggestion data.
|
||||
*/
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, REFRESH_SHORTCUT);
|
||||
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", REFRESH_SHORTCUT);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
Resources resources = getContext().getResources();
|
||||
Dictionary.getInstance().ensureLoaded(resources);
|
||||
mDictionary = new DictionaryDatabase(getContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the dictionary searches and suggestion queries from the Search Manager.
|
||||
* When requesting a specific word, the uri alone is required.
|
||||
* When searching all of the dictionary for matches, the selectionArgs argument must carry
|
||||
* the search query as the first element.
|
||||
* All other arguments are ignored.
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
if (!TextUtils.isEmpty(selection)) {
|
||||
throw new IllegalArgumentException("selection not allowed for " + uri);
|
||||
}
|
||||
if (selectionArgs != null && selectionArgs.length != 0) {
|
||||
throw new IllegalArgumentException("selectionArgs not allowed for " + uri);
|
||||
}
|
||||
if (!TextUtils.isEmpty(sortOrder)) {
|
||||
throw new IllegalArgumentException("sortOrder not allowed for " + uri);
|
||||
}
|
||||
|
||||
// Use the UriMatcher to see what kind of query we have and format the db query accordingly
|
||||
switch (sURIMatcher.match(uri)) {
|
||||
case SEARCH_SUGGEST:
|
||||
String query = null;
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
query = uri.getLastPathSegment().toLowerCase();
|
||||
if (selectionArgs == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"selectionArgs must be provided for the Uri: " + uri);
|
||||
}
|
||||
return getSuggestions(query, projection);
|
||||
case SHORTCUT_REFRESH:
|
||||
String shortcutId = null;
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
shortcutId = uri.getLastPathSegment();
|
||||
return getSuggestions(selectionArgs[0]);
|
||||
case SEARCH_WORDS:
|
||||
if (selectionArgs == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"selectionArgs must be provided for the Uri: " + uri);
|
||||
}
|
||||
return refreshShortcut(shortcutId, projection);
|
||||
return search(selectionArgs[0]);
|
||||
case GET_WORD:
|
||||
return getWord(uri);
|
||||
case REFRESH_SHORTCUT:
|
||||
return refreshShortcut(uri);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
||||
throw new IllegalArgumentException("Unknown Uri: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getSuggestions(String query, String[] projection) {
|
||||
String processedQuery = query == null ? "" : query.toLowerCase();
|
||||
List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
|
||||
private Cursor getSuggestions(String query) {
|
||||
query = query.toLowerCase();
|
||||
String[] columns = new String[] {
|
||||
BaseColumns._ID,
|
||||
DictionaryDatabase.KEY_WORD,
|
||||
DictionaryDatabase.KEY_DEFINITION,
|
||||
/* SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
|
||||
(only if you want to refresh shortcuts) */
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
|
||||
|
||||
MatrixCursor cursor = new MatrixCursor(COLUMNS);
|
||||
long id = 0;
|
||||
for (Dictionary.Word word : words) {
|
||||
cursor.addRow(columnValuesOfWord(id++, word));
|
||||
return mDictionary.getWordMatches(query, columns);
|
||||
}
|
||||
|
||||
return cursor;
|
||||
private Cursor search(String query) {
|
||||
query = query.toLowerCase();
|
||||
String[] columns = new String[] {
|
||||
BaseColumns._ID,
|
||||
DictionaryDatabase.KEY_WORD,
|
||||
DictionaryDatabase.KEY_DEFINITION};
|
||||
|
||||
return mDictionary.getWordMatches(query, columns);
|
||||
}
|
||||
|
||||
private Object[] columnValuesOfWord(long id, Dictionary.Word word) {
|
||||
return new Object[] {
|
||||
id, // _id
|
||||
word.word, // text1
|
||||
word.definition, // text2
|
||||
word.word, // intent_data (included when clicking on item)
|
||||
};
|
||||
private Cursor getWord(Uri uri) {
|
||||
String rowId = uri.getLastPathSegment();
|
||||
String[] columns = new String[] {
|
||||
DictionaryDatabase.KEY_WORD,
|
||||
DictionaryDatabase.KEY_DEFINITION};
|
||||
|
||||
return mDictionary.getWord(rowId, columns);
|
||||
}
|
||||
|
||||
private Cursor refreshShortcut(Uri uri) {
|
||||
/* This won't be called with the current implementation, but if we include
|
||||
* {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
|
||||
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
|
||||
* Quick Search Box. In which case, this method will query the table for the specific
|
||||
* word, using the given item Uri and provide all the columns originally provided with the
|
||||
* suggestion query.
|
||||
*/
|
||||
String rowId = uri.getLastPathSegment();
|
||||
String[] columns = new String[] {
|
||||
BaseColumns._ID,
|
||||
DictionaryDatabase.KEY_WORD,
|
||||
DictionaryDatabase.KEY_DEFINITION,
|
||||
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
|
||||
|
||||
return mDictionary.getWord(rowId, columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this is unused as is, but if we included
|
||||
* {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our results, we
|
||||
* could expect to receive refresh queries on this uri for the id provided, in which case we
|
||||
* would return a cursor with a single item representing the refreshed suggestion data.
|
||||
*/
|
||||
private Cursor refreshShortcut(String shortcutId, String[] projection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* All queries for this provider are for the search suggestion and shortcut refresh mime type.
|
||||
* This method is required in order to query the supported types.
|
||||
* It's also useful in our own query() method to determine the type of Uri received.
|
||||
*/
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
switch (sURIMatcher.match(uri)) {
|
||||
case SEARCH_WORDS:
|
||||
return WORDS_MIME_TYPE;
|
||||
case GET_WORD:
|
||||
return DEFINITION_MIME_TYPE;
|
||||
case SEARCH_SUGGEST:
|
||||
return SearchManager.SUGGEST_MIME_TYPE;
|
||||
case SHORTCUT_REFRESH:
|
||||
case REFRESH_SHORTCUT:
|
||||
return SearchManager.SHORTCUT_MIME_TYPE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
// Other required implementations...
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -18,138 +18,114 @@ package com.example.android.searchabledict;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SimpleCursorAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TwoLineListItem;
|
||||
|
||||
import java.util.List;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
/**
|
||||
* The main activity for the dictionary. Also displays search results triggered by the search
|
||||
* dialog.
|
||||
* The main activity for the dictionary.
|
||||
* Displays search results triggered by the search dialog and handles
|
||||
* actions from search suggestions.
|
||||
*/
|
||||
public class SearchableDictionary extends Activity {
|
||||
|
||||
private static final int MENU_SEARCH = 1;
|
||||
|
||||
private TextView mTextView;
|
||||
private ListView mList;
|
||||
private ListView mListView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
|
||||
mTextView = (TextView) findViewById(R.id.text);
|
||||
mListView = (ListView) findViewById(R.id.list);
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
setContentView(R.layout.main);
|
||||
mTextView = (TextView) findViewById(R.id.textField);
|
||||
mList = (ListView) findViewById(R.id.list);
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||
// from click on search results
|
||||
Dictionary.getInstance().ensureLoaded(getResources());
|
||||
String word = intent.getDataString();
|
||||
Dictionary.Word theWord = Dictionary.getInstance().getMatches(word).get(0);
|
||||
launchWord(theWord);
|
||||
// handles a click on a search suggestion; launches activity to show word
|
||||
Intent wordIntent = new Intent(this, WordActivity.class);
|
||||
wordIntent.setData(intent.getData());
|
||||
startActivity(wordIntent);
|
||||
finish();
|
||||
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||
// handles a search query
|
||||
String query = intent.getStringExtra(SearchManager.QUERY);
|
||||
mTextView.setText(getString(R.string.search_results, query));
|
||||
WordAdapter wordAdapter = new WordAdapter(Dictionary.getInstance().getMatches(query));
|
||||
mList.setAdapter(wordAdapter);
|
||||
mList.setOnItemClickListener(wordAdapter);
|
||||
showResults(query);
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("dict", intent.toString());
|
||||
if (intent.getExtras() != null) {
|
||||
Log.d("dict", intent.getExtras().keySet().toString());
|
||||
/**
|
||||
* Searches the dictionary and displays results for the given query.
|
||||
* @param query The search query
|
||||
*/
|
||||
private void showResults(String query) {
|
||||
|
||||
Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,
|
||||
new String[] {query}, null);
|
||||
|
||||
if (cursor == null) {
|
||||
// There are no results
|
||||
mTextView.setText(getString(R.string.no_results, new Object[] {query}));
|
||||
} else {
|
||||
// Display the number of results
|
||||
int count = cursor.getCount();
|
||||
String countString = getResources().getQuantityString(R.plurals.search_results,
|
||||
count, new Object[] {count, query});
|
||||
mTextView.setText(countString);
|
||||
|
||||
// Specify the columns we want to display in the result
|
||||
String[] from = new String[] { DictionaryDatabase.KEY_WORD,
|
||||
DictionaryDatabase.KEY_DEFINITION };
|
||||
|
||||
// Specify the corresponding layout elements where we want the columns to go
|
||||
int[] to = new int[] { R.id.word,
|
||||
R.id.definition };
|
||||
|
||||
// Create a simple cursor adapter for the definitions and apply them to the ListView
|
||||
SimpleCursorAdapter words = new SimpleCursorAdapter(this,
|
||||
R.layout.result, cursor, from, to);
|
||||
mListView.setAdapter(words);
|
||||
|
||||
// Define the on-click listener for the list items
|
||||
mListView.setOnItemClickListener(new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Build the Intent used to open WordActivity with a specific word Uri
|
||||
Intent wordIntent = new Intent(getApplicationContext(), WordActivity.class);
|
||||
Uri data = Uri.withAppendedPath(DictionaryProvider.CONTENT_URI,
|
||||
String.valueOf(id));
|
||||
wordIntent.setData(data);
|
||||
startActivity(wordIntent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
|
||||
.setIcon(android.R.drawable.ic_search_category_default)
|
||||
.setAlphabeticShortcut(SearchManager.MENU_KEY);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.options_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_SEARCH:
|
||||
case R.id.search:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void launchWord(Dictionary.Word theWord) {
|
||||
Intent next = new Intent();
|
||||
next.setClass(this, WordActivity.class);
|
||||
next.putExtra("word", theWord.word);
|
||||
next.putExtra("definition", theWord.definition);
|
||||
startActivity(next);
|
||||
}
|
||||
|
||||
class WordAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
|
||||
|
||||
private final List<Dictionary.Word> mWords;
|
||||
private final LayoutInflater mInflater;
|
||||
|
||||
public WordAdapter(List<Dictionary.Word> words) {
|
||||
mWords = words;
|
||||
mInflater = (LayoutInflater) SearchableDictionary.this.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mWords.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TwoLineListItem view = (convertView != null) ? (TwoLineListItem) convertView :
|
||||
createView(parent);
|
||||
bindView(view, mWords.get(position));
|
||||
return view;
|
||||
}
|
||||
|
||||
private TwoLineListItem createView(ViewGroup parent) {
|
||||
TwoLineListItem item = (TwoLineListItem) mInflater.inflate(
|
||||
android.R.layout.simple_list_item_2, parent, false);
|
||||
item.getText2().setSingleLine();
|
||||
item.getText2().setEllipsize(TextUtils.TruncateAt.END);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void bindView(TwoLineListItem view, Dictionary.Word word) {
|
||||
view.getText1().setText(word.word);
|
||||
view.getText2().setText(word.definition);
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
launchWord(mWords.get(position));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -17,33 +17,58 @@
|
||||
package com.example.android.searchabledict;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Displays a word and its definition.
|
||||
*/
|
||||
public class WordActivity extends Activity {
|
||||
|
||||
private TextView mWord;
|
||||
private TextView mDefinition;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.word);
|
||||
|
||||
mWord = (TextView) findViewById(R.id.word);
|
||||
mDefinition = (TextView) findViewById(R.id.definition);
|
||||
Uri uri = getIntent().getData();
|
||||
Cursor cursor = managedQuery(uri, null, null, null, null);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (cursor == null) {
|
||||
finish();
|
||||
} else {
|
||||
cursor.moveToFirst();
|
||||
|
||||
String word = intent.getStringExtra("word");
|
||||
String definition = intent.getStringExtra("definition");
|
||||
TextView word = (TextView) findViewById(R.id.word);
|
||||
TextView definition = (TextView) findViewById(R.id.definition);
|
||||
|
||||
mWord.setText(word);
|
||||
mDefinition.setText(definition);
|
||||
int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
|
||||
int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);
|
||||
|
||||
word.setText(cursor.getString(wIndex));
|
||||
definition.setText(cursor.getString(dIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.options_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.search:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user