Reinclude SearchableDictionary sample app as a demo of third party inclusion in Quick Search Box.
This reverts commit 3e83e8bc8b.
This commit is contained in:
committed by
Android Git Automerger
parent
b8ff7ade69
commit
83e2eae3fb
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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,160 @@
|
||||
/*
|
||||
* 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.app.SearchManager;
|
||||
import android.content.ContentProvider;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Provides search suggestions for a list of words and their definitions.
|
||||
*/
|
||||
public class DictionaryProvider extends ContentProvider {
|
||||
|
||||
public static String AUTHORITY = "dictionary";
|
||||
|
||||
private static final int SEARCH_SUGGEST = 0;
|
||||
private static final int SHORTCUT_REFRESH = 1;
|
||||
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.
|
||||
*/
|
||||
private static UriMatcher buildUriMatcher() {
|
||||
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
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);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
Resources resources = getContext().getResources();
|
||||
Dictionary.getInstance().ensureLoaded(resources);
|
||||
return true;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
switch (sURIMatcher.match(uri)) {
|
||||
case SEARCH_SUGGEST:
|
||||
String query = null;
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
query = uri.getLastPathSegment().toLowerCase();
|
||||
}
|
||||
return getSuggestions(query, projection);
|
||||
case SHORTCUT_REFRESH:
|
||||
String shortcutId = null;
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
shortcutId = uri.getLastPathSegment();
|
||||
}
|
||||
return refreshShortcut(shortcutId, projection);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getSuggestions(String query, String[] projection) {
|
||||
String processedQuery = query == null ? "" : query.toLowerCase();
|
||||
List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
|
||||
|
||||
MatrixCursor cursor = new MatrixCursor(COLUMNS);
|
||||
for (Dictionary.Word word : words) {
|
||||
cursor.addRow(columnValuesOfWord(word));
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private Object[] columnValuesOfWord(Dictionary.Word word) {
|
||||
return new String[] {
|
||||
word.word, // _id
|
||||
word.word, // text1
|
||||
word.definition, // text2
|
||||
word.word, // intent_data (included when clicking on item)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public String getType(Uri uri) {
|
||||
switch (sURIMatcher.match(uri)) {
|
||||
case SEARCH_SUGGEST:
|
||||
return SearchManager.SUGGEST_MIME_TYPE;
|
||||
case SHORTCUT_REFRESH:
|
||||
return SearchManager.SHORTCUT_MIME_TYPE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.MenuItem;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TwoLineListItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The main activity for the dictionary. Also displays search results triggered by the search
|
||||
* dialog.
|
||||
*/
|
||||
public class SearchableDictionary extends Activity {
|
||||
|
||||
private static final int MENU_SEARCH = 1;
|
||||
|
||||
private TextView mTextView;
|
||||
private ListView mList;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
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);
|
||||
finish();
|
||||
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||
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);
|
||||
}
|
||||
|
||||
Log.d("dict", intent.toString());
|
||||
if (intent.getExtras() != null) {
|
||||
Log.d("dict", intent.getExtras().keySet().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.app.Activity;
|
||||
import android.os.Bundle;
|
||||
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);
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
String word = intent.getStringExtra("word");
|
||||
String definition = intent.getStringExtra("definition");
|
||||
|
||||
mWord.setText(word);
|
||||
mDefinition.setText(definition);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user