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"?>
|
<?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");
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with the License.
|
** you may not use this file except in compliance with the License.
|
||||||
@@ -22,12 +22,11 @@
|
|||||||
<uses-sdk android:minSdkVersion="4" />
|
<uses-sdk android:minSdkVersion="4" />
|
||||||
|
|
||||||
<application android:label="@string/app_name"
|
<application android:label="@string/app_name"
|
||||||
android:icon="@drawable/ic_dictionary">
|
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"
|
<activity android:name=".SearchableDictionary"
|
||||||
android:label="@string/app_name"
|
android:theme="@android:style/Theme.NoTitleBar">
|
||||||
android:theme="@android:style/Theme.NoTitleBar">
|
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@@ -37,22 +36,26 @@
|
|||||||
<!-- Receives the search request. -->
|
<!-- Receives the search request. -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<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>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Points to searchable meta data. -->
|
<!-- Points to searchable meta data. -->
|
||||||
<meta-data android:name="android.app.searchable"
|
<meta-data android:name="android.app.searchable"
|
||||||
android:resource="@xml/searchable"/>
|
android:resource="@xml/searchable" />
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- Displays the definition of a word. -->
|
<!-- Displays the definition of a word. -->
|
||||||
<activity android:name=".WordActivity"
|
<activity android:name=".WordActivity"
|
||||||
android:theme="@android:style/Theme.NoTitleBar"/>
|
android:theme="@android:style/Theme.NoTitleBar" />
|
||||||
|
|
||||||
<!-- Provides search suggestions for words and their definitions. -->
|
<!-- Provides search suggestions for words and their definitions. -->
|
||||||
<provider android:name="DictionaryProvider"
|
<provider android:name=".DictionaryProvider"
|
||||||
android:authorities="dictionary"
|
android:authorities="com.example.android.searchabledict.DictionaryProvider" />
|
||||||
android:syncable="false" />
|
|
||||||
|
<!-- Points to searchable activity so the whole app can invoke search. -->
|
||||||
|
<meta-data android:name="android.app.default_searchable"
|
||||||
|
android:value=".SearchableDictionary" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,21 +1,32 @@
|
|||||||
<p>A sample application that demonstrates Android's search framework.</p>
|
<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
|
<p>This application includes a dictionary of words. By invoking the Android search dialog inside the
|
||||||
(via the device search button or Menu > Search), a local search will be performed
|
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
|
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>
|
that match the entered text.</p>
|
||||||
|
|
||||||
<p>The application also grants content provider privileges to
|
<p>The application also grants content provider privileges to
|
||||||
Quick Search Box, Android's system-wide search tool. This means that the
|
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,
|
dictionary definitions can be offered as search suggestions outside of the application,
|
||||||
when text is entered into Quick Search Box.</p>
|
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>
|
<p>See also:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="../../../reference/android/app/SearchManager.html">SearchManager</a></li>
|
<li><a href="../../../guide/topics/search/index.html">Search Developer Guide</a></li>
|
||||||
<li><a href="../../../reference/android/content/ContentProvider.html">ContentProvider</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<img src="../images/SearchableDictionary1.png" />
|
<img src="../images/SearchableDictionary1.png" />
|
||||||
<img src="../images/SearchableDictionary2.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"?>
|
<?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");
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with the License.
|
** you may not use this file except in compliance with the License.
|
||||||
@@ -16,22 +16,25 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
<!-- Layout for SearchableActivity.
|
||||||
|
-->
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="fill_parent">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textField"
|
android:id="@+id/text"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textColor="?android:textColorPrimary"
|
||||||
android:layout_width="match_parent"
|
android:textSize="17dp"
|
||||||
android:layout_height="wrap_content"
|
android:text="@string/search_instructions"
|
||||||
android:text="@string/search_instructions"/>
|
android:background="@android:drawable/title_bar"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/list"
|
android:id="@+id/list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="0dip"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1" />
|
||||||
</LinearLayout>
|
</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"?>
|
<?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");
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with the License.
|
** you may not use this file except in compliance with the License.
|
||||||
@@ -16,24 +16,24 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
<!-- Layout for WordActivity.
|
||||||
|
-->
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="5dp">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/word"
|
android:id="@+id/word"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textSize="35dp"
|
||||||
android:textSize="30sp"
|
android:textColor="?android:textColorPrimary"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:text="@string/search_instructions"
|
|
||||||
android:layout_marginLeft="10dip"
|
|
||||||
android:layout_marginTop="5dip" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/definition"
|
android:id="@+id/definition"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textSize="18dp"
|
||||||
android:layout_width="match_parent"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:layout_height="wrap_content"
|
android:paddingTop="10dp"
|
||||||
android:text="@string/search_instructions"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginLeft="10dip" />
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</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
|
analyst - n. someone who is skilled at analyzing data
|
||||||
analyze - v. break down into components or essential features
|
analyze - v. break down into components or essential features
|
||||||
anchor - v. fix firmly and stably
|
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
|
annual - j. occurring or payable every year
|
||||||
anonymous - j. having no known name or identity or known source
|
anonymous - j. having no known name or identity or known source
|
||||||
antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
|
antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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");
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with the License.
|
** you may not use this file except in compliance with the License.
|
||||||
@@ -16,14 +16,17 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources>
|
||||||
|
|
||||||
<!-- The name of the application. -->
|
<!-- The name of the application. -->
|
||||||
<string name="app_name">Searchable Dictionary</string>
|
<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>
|
<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. -->
|
<!-- The menu entry that invokes search. -->
|
||||||
<string name="menu_search">Search</string>
|
<string name="menu_search">Search</string>
|
||||||
|
|
||||||
@@ -34,5 +37,12 @@
|
|||||||
<string name="search_instructions">Press the search key to look up a word</string>
|
<string name="search_instructions">Press the search key to look up a word</string>
|
||||||
|
|
||||||
<!-- Shown above search results when we receive a search request. -->
|
<!-- 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>
|
</resources>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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");
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with 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 attributes below configure the Android search box appearance
|
||||||
- the 'label' points to a short description used when searching within the application if
|
and the search suggestions settings.
|
||||||
'badge mode' is used as specified with android:searchMode="useLabelAsBadge" (which it is not for
|
See the Developer Guide for more information
|
||||||
this application).
|
http://developer.android.com/guide/topics/search/
|
||||||
- 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.
|
|
||||||
-->
|
-->
|
||||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:label="@string/search_label"
|
android:label="@string/search_label"
|
||||||
|
android:hint="@string/search_hint"
|
||||||
android:searchSettingsDescription="@string/settings_description"
|
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: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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.app.SearchManager;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.UriMatcher;
|
import android.content.UriMatcher;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides search suggestions for a list of words and their definitions.
|
* Provides access to the dictionary database.
|
||||||
*/
|
*/
|
||||||
public class DictionaryProvider extends ContentProvider {
|
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;
|
// MIME types used for searching words or looking up a single definition
|
||||||
private static final int SHORTCUT_REFRESH = 1;
|
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();
|
private static final UriMatcher sURIMatcher = buildUriMatcher();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The columns we'll include in our search suggestions. There are others that could be used
|
* Builds up a UriMatcher for search suggestion and shortcut refresh queries.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
private static UriMatcher buildUriMatcher() {
|
private static UriMatcher buildUriMatcher() {
|
||||||
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
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_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;
|
return matcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
Resources resources = getContext().getResources();
|
mDictionary = new DictionaryDatabase(getContext());
|
||||||
Dictionary.getInstance().ensureLoaded(resources);
|
|
||||||
return true;
|
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
|
@Override
|
||||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||||
String sortOrder) {
|
String sortOrder) {
|
||||||
if (!TextUtils.isEmpty(selection)) {
|
|
||||||
throw new IllegalArgumentException("selection not allowed for " + uri);
|
// Use the UriMatcher to see what kind of query we have and format the db query accordingly
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
switch (sURIMatcher.match(uri)) {
|
switch (sURIMatcher.match(uri)) {
|
||||||
case SEARCH_SUGGEST:
|
case SEARCH_SUGGEST:
|
||||||
String query = null;
|
if (selectionArgs == null) {
|
||||||
if (uri.getPathSegments().size() > 1) {
|
throw new IllegalArgumentException(
|
||||||
query = uri.getLastPathSegment().toLowerCase();
|
"selectionArgs must be provided for the Uri: " + uri);
|
||||||
}
|
}
|
||||||
return getSuggestions(query, projection);
|
return getSuggestions(selectionArgs[0]);
|
||||||
case SHORTCUT_REFRESH:
|
case SEARCH_WORDS:
|
||||||
String shortcutId = null;
|
if (selectionArgs == null) {
|
||||||
if (uri.getPathSegments().size() > 1) {
|
throw new IllegalArgumentException(
|
||||||
shortcutId = uri.getLastPathSegment();
|
"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:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
throw new IllegalArgumentException("Unknown Uri: " + uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor getSuggestions(String query, String[] projection) {
|
private Cursor getSuggestions(String query) {
|
||||||
String processedQuery = query == null ? "" : query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
|
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);
|
return mDictionary.getWordMatches(query, columns);
|
||||||
long id = 0;
|
|
||||||
for (Dictionary.Word word : words) {
|
|
||||||
cursor.addRow(columnValuesOfWord(id++, word));
|
|
||||||
}
|
|
||||||
|
|
||||||
return cursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[] columnValuesOfWord(long id, Dictionary.Word word) {
|
private Cursor search(String query) {
|
||||||
return new Object[] {
|
query = query.toLowerCase();
|
||||||
id, // _id
|
String[] columns = new String[] {
|
||||||
word.word, // text1
|
BaseColumns._ID,
|
||||||
word.definition, // text2
|
DictionaryDatabase.KEY_WORD,
|
||||||
word.word, // intent_data (included when clicking on item)
|
DictionaryDatabase.KEY_DEFINITION};
|
||||||
};
|
|
||||||
|
return mDictionary.getWordMatches(query, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* This method is required in order to query the supported types.
|
||||||
* {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our results, we
|
* It's also useful in our own query() method to determine the type of Uri received.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getType(Uri uri) {
|
public String getType(Uri uri) {
|
||||||
switch (sURIMatcher.match(uri)) {
|
switch (sURIMatcher.match(uri)) {
|
||||||
|
case SEARCH_WORDS:
|
||||||
|
return WORDS_MIME_TYPE;
|
||||||
|
case GET_WORD:
|
||||||
|
return DEFINITION_MIME_TYPE;
|
||||||
case SEARCH_SUGGEST:
|
case SEARCH_SUGGEST:
|
||||||
return SearchManager.SUGGEST_MIME_TYPE;
|
return SearchManager.SUGGEST_MIME_TYPE;
|
||||||
case SHORTCUT_REFRESH:
|
case REFRESH_SHORTCUT:
|
||||||
return SearchManager.SHORTCUT_MIME_TYPE;
|
return SearchManager.SHORTCUT_MIME_TYPE;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
throw new IllegalArgumentException("Unknown URL " + uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Other required implementations...
|
||||||
|
|
||||||
|
@Override
|
||||||
public Uri insert(Uri uri, ContentValues values) {
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
throw new UnsupportedOperationException();
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Activity;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
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.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.SimpleCursorAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.TwoLineListItem;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main activity for the dictionary. Also displays search results triggered by the search
|
* The main activity for the dictionary.
|
||||||
* dialog.
|
* Displays search results triggered by the search dialog and handles
|
||||||
|
* actions from search suggestions.
|
||||||
*/
|
*/
|
||||||
public class SearchableDictionary extends Activity {
|
public class SearchableDictionary extends Activity {
|
||||||
|
|
||||||
private static final int MENU_SEARCH = 1;
|
|
||||||
|
|
||||||
private TextView mTextView;
|
private TextView mTextView;
|
||||||
private ListView mList;
|
private ListView mListView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
|
mTextView = (TextView) findViewById(R.id.text);
|
||||||
|
mListView = (ListView) findViewById(R.id.list);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
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())) {
|
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||||
// from click on search results
|
// handles a click on a search suggestion; launches activity to show word
|
||||||
Dictionary.getInstance().ensureLoaded(getResources());
|
Intent wordIntent = new Intent(this, WordActivity.class);
|
||||||
String word = intent.getDataString();
|
wordIntent.setData(intent.getData());
|
||||||
Dictionary.Word theWord = Dictionary.getInstance().getMatches(word).get(0);
|
startActivity(wordIntent);
|
||||||
launchWord(theWord);
|
|
||||||
finish();
|
finish();
|
||||||
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||||
|
// handles a search query
|
||||||
String query = intent.getStringExtra(SearchManager.QUERY);
|
String query = intent.getStringExtra(SearchManager.QUERY);
|
||||||
mTextView.setText(getString(R.string.search_results, query));
|
showResults(query);
|
||||||
WordAdapter wordAdapter = new WordAdapter(Dictionary.getInstance().getMatches(query));
|
|
||||||
mList.setAdapter(wordAdapter);
|
|
||||||
mList.setOnItemClickListener(wordAdapter);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.d("dict", intent.toString());
|
/**
|
||||||
if (intent.getExtras() != null) {
|
* Searches the dictionary and displays results for the given query.
|
||||||
Log.d("dict", intent.getExtras().keySet().toString());
|
* @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
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
|
MenuInflater inflater = getMenuInflater();
|
||||||
.setIcon(android.R.drawable.ic_search_category_default)
|
inflater.inflate(R.menu.options_menu, menu);
|
||||||
.setAlphabeticShortcut(SearchManager.MENU_KEY);
|
return true;
|
||||||
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case MENU_SEARCH:
|
case R.id.search:
|
||||||
onSearchRequested();
|
onSearchRequested();
|
||||||
return true;
|
return true;
|
||||||
}
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,33 +17,58 @@
|
|||||||
package com.example.android.searchabledict;
|
package com.example.android.searchabledict;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a word and its definition.
|
* Displays a word and its definition.
|
||||||
*/
|
*/
|
||||||
public class WordActivity extends Activity {
|
public class WordActivity extends Activity {
|
||||||
|
|
||||||
private TextView mWord;
|
|
||||||
private TextView mDefinition;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.word);
|
setContentView(R.layout.word);
|
||||||
|
|
||||||
mWord = (TextView) findViewById(R.id.word);
|
Uri uri = getIntent().getData();
|
||||||
mDefinition = (TextView) findViewById(R.id.definition);
|
Cursor cursor = managedQuery(uri, null, null, null, null);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
if (cursor == null) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
cursor.moveToFirst();
|
||||||
|
|
||||||
String word = intent.getStringExtra("word");
|
TextView word = (TextView) findViewById(R.id.word);
|
||||||
String definition = intent.getStringExtra("definition");
|
TextView definition = (TextView) findViewById(R.id.definition);
|
||||||
|
|
||||||
mWord.setText(word);
|
int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
|
||||||
mDefinition.setText(definition);
|
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