Merge change 1063 into donut
* changes: Refactor SearchableInfo.
This commit is contained in:
@ -601,11 +601,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
CharSequence text = null;
|
CharSequence text = null;
|
||||||
|
|
||||||
// optionally show one or the other.
|
// optionally show one or the other.
|
||||||
if (mSearchable.mBadgeIcon) {
|
if (mSearchable.useBadgeIcon()) {
|
||||||
icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
|
icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
|
||||||
visibility = View.VISIBLE;
|
visibility = View.VISIBLE;
|
||||||
if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
|
if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
|
||||||
} else if (mSearchable.mBadgeLabel) {
|
} else if (mSearchable.useBadgeLabel()) {
|
||||||
text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
|
text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
|
||||||
visibility = View.VISIBLE;
|
visibility = View.VISIBLE;
|
||||||
if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
|
if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
|
||||||
@ -714,8 +714,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
// if it's an action specified by the searchable activity, launch the
|
// if it's an action specified by the searchable activity, launch the
|
||||||
// entered query with the action key
|
// entered query with the action key
|
||||||
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
||||||
if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
|
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
|
||||||
launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
|
launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,7 +830,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
// in the voice search system. We have to keep the bundle separate,
|
// in the voice search system. We have to keep the bundle separate,
|
||||||
// because it becomes immutable once it enters the PendingIntent
|
// because it becomes immutable once it enters the PendingIntent
|
||||||
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
|
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
|
||||||
queryIntent.setComponent(mSearchable.mSearchActivity);
|
queryIntent.setComponent(mSearchable.getSearchActivity());
|
||||||
PendingIntent pending = PendingIntent.getActivity(
|
PendingIntent pending = PendingIntent.getActivity(
|
||||||
getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
|
getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
@ -913,8 +913,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
}
|
}
|
||||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||||
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
||||||
if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
|
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
|
||||||
launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
|
launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1010,8 +1010,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
// Next, check for an "action key"
|
// Next, check for an "action key"
|
||||||
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
|
||||||
if ((actionKey != null) &&
|
if ((actionKey != null) &&
|
||||||
((actionKey.mSuggestActionMsg != null) ||
|
((actionKey.getSuggestActionMsg() != null) ||
|
||||||
(actionKey.mSuggestActionMsgColumn != null))) {
|
(actionKey.getSuggestActionMsgColumn() != null))) {
|
||||||
// launch suggestion using action key column
|
// launch suggestion using action key column
|
||||||
int position = mSearchAutoComplete.getListSelection();
|
int position = mSearchAutoComplete.getListSelection();
|
||||||
if (position != ListView.INVALID_POSITION) {
|
if (position != ListView.INVALID_POSITION) {
|
||||||
@ -1292,7 +1292,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
|
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
|
||||||
}
|
}
|
||||||
// attempt to enforce security requirement (no 3rd-party intents)
|
// attempt to enforce security requirement (no 3rd-party intents)
|
||||||
intent.setComponent(mSearchable.mSearchActivity);
|
intent.setComponent(mSearchable.getSearchActivity());
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1308,14 +1308,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
|
private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
|
||||||
String result = null;
|
String result = null;
|
||||||
// check first in the cursor data, for a suggestion-specific message
|
// check first in the cursor data, for a suggestion-specific message
|
||||||
final String column = actionKey.mSuggestActionMsgColumn;
|
final String column = actionKey.getSuggestActionMsgColumn();
|
||||||
if (column != null) {
|
if (column != null) {
|
||||||
result = SuggestionsAdapter.getColumnString(c, column);
|
result = SuggestionsAdapter.getColumnString(c, column);
|
||||||
}
|
}
|
||||||
// If the cursor didn't give us a message, see if there's a single message defined
|
// If the cursor didn't give us a message, see if there's a single message defined
|
||||||
// for the actionkey (for all suggestions)
|
// for the actionkey (for all suggestions)
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = actionKey.mSuggestActionMsg;
|
result = actionKey.getSuggestActionMsg();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1602,7 +1602,7 @@ public class SearchManager
|
|||||||
public static boolean isDefaultSearchable(SearchableInfo searchable) {
|
public static boolean isDefaultSearchable(SearchableInfo searchable) {
|
||||||
SearchableInfo defaultSearchable = SearchManager.getSearchableInfo(null, true);
|
SearchableInfo defaultSearchable = SearchManager.getSearchableInfo(null, true);
|
||||||
return defaultSearchable != null
|
return defaultSearchable != null
|
||||||
&& defaultSearchable.mSearchActivity.equals(searchable.mSearchActivity);
|
&& defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,14 +231,14 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSearchable.mQueryRewriteFromData) {
|
if (mSearchable.shouldRewriteQueryFromData()) {
|
||||||
String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
|
String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSearchable.mQueryRewriteFromText) {
|
if (mSearchable.shouldRewriteQueryFromText()) {
|
||||||
String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
|
String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
|
||||||
if (text1 != null) {
|
if (text1 != null) {
|
||||||
return text1;
|
return text1;
|
||||||
|
@ -35,14 +35,13 @@ import android.util.Xml;
|
|||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
public final class SearchableInfo implements Parcelable {
|
public final class SearchableInfo implements Parcelable {
|
||||||
|
|
||||||
// general debugging support
|
// general debugging support
|
||||||
final static String LOG_TAG = "SearchableInfo";
|
private static final boolean DBG = true;
|
||||||
|
private static final String LOG_TAG = "SearchableInfo";
|
||||||
// set this flag to 1 to prevent any apps from providing suggestions
|
|
||||||
final static int DBG_INHIBIT_SUGGESTIONS = 0;
|
|
||||||
|
|
||||||
// static strings used for XML lookups.
|
// static strings used for XML lookups.
|
||||||
// TODO how should these be documented for the developer, in a more structured way than
|
// TODO how should these be documented for the developer, in a more structured way than
|
||||||
@ -51,40 +50,44 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
private static final String MD_XML_ELEMENT_SEARCHABLE = "searchable";
|
private static final String MD_XML_ELEMENT_SEARCHABLE = "searchable";
|
||||||
private static final String MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY = "actionkey";
|
private static final String MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY = "actionkey";
|
||||||
|
|
||||||
|
// flags in the searchMode attribute
|
||||||
|
private static final int SEARCH_MODE_BADGE_LABEL = 0x04;
|
||||||
|
private static final int SEARCH_MODE_BADGE_ICON = 0x08;
|
||||||
|
private static final int SEARCH_MODE_QUERY_REWRITE_FROM_DATA = 0x10;
|
||||||
|
private static final int SEARCH_MODE_QUERY_REWRITE_FROM_TEXT = 0x20;
|
||||||
|
|
||||||
// true member variables - what we know about the searchability
|
// true member variables - what we know about the searchability
|
||||||
// TO-DO replace public with getters
|
private final int mLabelId;
|
||||||
public boolean mSearchable = false;
|
private final ComponentName mSearchActivity;
|
||||||
private int mLabelId = 0;
|
private final int mHintId;
|
||||||
public ComponentName mSearchActivity = null;
|
private final int mSearchMode;
|
||||||
private int mHintId = 0;
|
private final int mIconId;
|
||||||
private int mSearchMode = 0;
|
private final int mSearchButtonText;
|
||||||
public boolean mBadgeLabel = false;
|
private final int mSearchInputType;
|
||||||
public boolean mBadgeIcon = false;
|
private final int mSearchImeOptions;
|
||||||
public boolean mQueryRewriteFromData = false;
|
private final boolean mIncludeInGlobalSearch;
|
||||||
public boolean mQueryRewriteFromText = false;
|
private final String mSuggestAuthority;
|
||||||
private int mIconId = 0;
|
private final String mSuggestPath;
|
||||||
private int mSearchButtonText = 0;
|
private final String mSuggestSelection;
|
||||||
private int mSearchInputType = 0;
|
private final String mSuggestIntentAction;
|
||||||
private int mSearchImeOptions = 0;
|
private final String mSuggestIntentData;
|
||||||
private boolean mIncludeInGlobalSearch = false;
|
private final int mSuggestThreshold;
|
||||||
private String mSuggestAuthority = null;
|
// Maps key codes to action key information. auto-boxing is not so bad here,
|
||||||
private String mSuggestPath = null;
|
// since keycodes for the hard keys are < 127. For such values, Integer.valueOf()
|
||||||
private String mSuggestSelection = null;
|
// uses shared Integer objects.
|
||||||
private String mSuggestIntentAction = null;
|
// This is not final, to allow lazy initialization.
|
||||||
private String mSuggestIntentData = null;
|
private HashMap<Integer,ActionKeyInfo> mActionKeys = null;
|
||||||
private int mSuggestThreshold = 0;
|
private final String mSuggestProviderPackage;
|
||||||
private ActionKeyInfo mActionKeyList = null;
|
|
||||||
private String mSuggestProviderPackage = null;
|
|
||||||
|
|
||||||
// Flag values for Searchable_voiceSearchMode
|
// Flag values for Searchable_voiceSearchMode
|
||||||
private static int VOICE_SEARCH_SHOW_BUTTON = 1;
|
private static int VOICE_SEARCH_SHOW_BUTTON = 1;
|
||||||
private static int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2;
|
private static int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2;
|
||||||
private static int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4;
|
private static int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4;
|
||||||
private int mVoiceSearchMode = 0;
|
private final int mVoiceSearchMode;
|
||||||
private int mVoiceLanguageModeId; // voiceLanguageModel
|
private final int mVoiceLanguageModeId; // voiceLanguageModel
|
||||||
private int mVoicePromptTextId; // voicePromptText
|
private final int mVoicePromptTextId; // voicePromptText
|
||||||
private int mVoiceLanguageId; // voiceLanguage
|
private final int mVoiceLanguageId; // voiceLanguage
|
||||||
private int mVoiceMaxResults; // voiceMaxResults
|
private final int mVoiceMaxResults; // voiceMaxResults
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,6 +99,41 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
return mSuggestAuthority;
|
return mSuggestAuthority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the component name of the searchable activity.
|
||||||
|
*/
|
||||||
|
public ComponentName getSearchActivity() {
|
||||||
|
return mSearchActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the badge should be a text label.
|
||||||
|
*/
|
||||||
|
public boolean useBadgeLabel() {
|
||||||
|
return 0 != (mSearchMode & SEARCH_MODE_BADGE_LABEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the badge should be an icon.
|
||||||
|
*/
|
||||||
|
public boolean useBadgeIcon() {
|
||||||
|
return (0 != (mSearchMode & SEARCH_MODE_BADGE_ICON)) && (mIconId != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the text in the query field should come from the suggestion intent data.
|
||||||
|
*/
|
||||||
|
public boolean shouldRewriteQueryFromData() {
|
||||||
|
return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the text in the query field should come from the suggestion title.
|
||||||
|
*/
|
||||||
|
public boolean shouldRewriteQueryFromText() {
|
||||||
|
return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the path for obtaining search suggestions.
|
* Retrieve the path for obtaining search suggestions.
|
||||||
*
|
*
|
||||||
@ -218,10 +256,9 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
* @param attr The attribute set we found in the XML file, contains the values that are used to
|
* @param attr The attribute set we found in the XML file, contains the values that are used to
|
||||||
* construct the object.
|
* construct the object.
|
||||||
* @param cName The component name of the searchable activity
|
* @param cName The component name of the searchable activity
|
||||||
|
* @throws IllegalArgumentException if the searchability info is invalid or insufficient
|
||||||
*/
|
*/
|
||||||
private SearchableInfo(Context activityContext, AttributeSet attr, final ComponentName cName) {
|
private SearchableInfo(Context activityContext, AttributeSet attr, final ComponentName cName) {
|
||||||
// initialize as an "unsearchable" object
|
|
||||||
mSearchable = false;
|
|
||||||
mSearchActivity = cName;
|
mSearchActivity = cName;
|
||||||
|
|
||||||
TypedArray a = activityContext.obtainStyledAttributes(attr,
|
TypedArray a = activityContext.obtainStyledAttributes(attr,
|
||||||
@ -240,21 +277,19 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
mIncludeInGlobalSearch = a.getBoolean(
|
mIncludeInGlobalSearch = a.getBoolean(
|
||||||
com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false);
|
com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false);
|
||||||
|
|
||||||
setSearchModeFlags();
|
mSuggestAuthority = a.getString(
|
||||||
if (DBG_INHIBIT_SUGGESTIONS == 0) {
|
com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
|
||||||
mSuggestAuthority = a.getString(
|
mSuggestPath = a.getString(
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
|
com.android.internal.R.styleable.Searchable_searchSuggestPath);
|
||||||
mSuggestPath = a.getString(
|
mSuggestSelection = a.getString(
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestPath);
|
com.android.internal.R.styleable.Searchable_searchSuggestSelection);
|
||||||
mSuggestSelection = a.getString(
|
mSuggestIntentAction = a.getString(
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestSelection);
|
com.android.internal.R.styleable.Searchable_searchSuggestIntentAction);
|
||||||
mSuggestIntentAction = a.getString(
|
mSuggestIntentData = a.getString(
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestIntentAction);
|
com.android.internal.R.styleable.Searchable_searchSuggestIntentData);
|
||||||
mSuggestIntentData = a.getString(
|
mSuggestThreshold = a.getInt(
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestIntentData);
|
com.android.internal.R.styleable.Searchable_searchSuggestThreshold, 0);
|
||||||
mSuggestThreshold = a.getInt(
|
|
||||||
com.android.internal.R.styleable.Searchable_searchSuggestThreshold, 0);
|
|
||||||
}
|
|
||||||
mVoiceSearchMode =
|
mVoiceSearchMode =
|
||||||
a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0);
|
a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0);
|
||||||
// TODO this didn't work - came back zero from YouTube
|
// TODO this didn't work - came back zero from YouTube
|
||||||
@ -270,44 +305,31 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
a.recycle();
|
a.recycle();
|
||||||
|
|
||||||
// get package info for suggestions provider (if any)
|
// get package info for suggestions provider (if any)
|
||||||
|
String suggestProviderPackage = null;
|
||||||
if (mSuggestAuthority != null) {
|
if (mSuggestAuthority != null) {
|
||||||
PackageManager pm = activityContext.getPackageManager();
|
PackageManager pm = activityContext.getPackageManager();
|
||||||
ProviderInfo pi = pm.resolveContentProvider(mSuggestAuthority, 0);
|
ProviderInfo pi = pm.resolveContentProvider(mSuggestAuthority, 0);
|
||||||
if (pi != null) {
|
if (pi != null) {
|
||||||
mSuggestProviderPackage = pi.packageName;
|
suggestProviderPackage = pi.packageName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSuggestProviderPackage = suggestProviderPackage;
|
||||||
|
|
||||||
// for now, implement some form of rules - minimal data
|
// for now, implement some form of rules - minimal data
|
||||||
if (mLabelId != 0) {
|
if (mLabelId == 0) {
|
||||||
mSearchable = true;
|
throw new IllegalArgumentException("No label.");
|
||||||
} else {
|
|
||||||
// Provide some help for developers instead of just silently discarding
|
|
||||||
Log.w(LOG_TAG, "Insufficient metadata to configure searchability for " +
|
|
||||||
cName.flattenToShortString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert searchmode to flags.
|
|
||||||
*/
|
|
||||||
private void setSearchModeFlags() {
|
|
||||||
mBadgeLabel = (0 != (mSearchMode & 4));
|
|
||||||
mBadgeIcon = (0 != (mSearchMode & 8)) && (mIconId != 0);
|
|
||||||
mQueryRewriteFromData = (0 != (mSearchMode & 0x10));
|
|
||||||
mQueryRewriteFromText = (0 != (mSearchMode & 0x20));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private class used to hold the "action key" configuration
|
* Private class used to hold the "action key" configuration
|
||||||
*/
|
*/
|
||||||
public static class ActionKeyInfo implements Parcelable {
|
public static class ActionKeyInfo implements Parcelable {
|
||||||
|
|
||||||
public int mKeyCode = 0;
|
private final int mKeyCode;
|
||||||
public String mQueryActionMsg;
|
private final String mQueryActionMsg;
|
||||||
public String mSuggestActionMsg;
|
private final String mSuggestActionMsg;
|
||||||
public String mSuggestActionMsgColumn;
|
private final String mSuggestActionMsgColumn;
|
||||||
private ActionKeyInfo mNext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create one object using attributeset as input data.
|
* Create one object using attributeset as input data.
|
||||||
@ -315,10 +337,9 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
* is about.
|
* is about.
|
||||||
* @param attr The attribute set we found in the XML file, contains the values that are used to
|
* @param attr The attribute set we found in the XML file, contains the values that are used to
|
||||||
* construct the object.
|
* construct the object.
|
||||||
* @param next We'll build these up using a simple linked list (since there are usually
|
* @throws IllegalArgumentException if the action key configuration is invalid
|
||||||
* just zero or one).
|
|
||||||
*/
|
*/
|
||||||
public ActionKeyInfo(Context activityContext, AttributeSet attr, ActionKeyInfo next) {
|
public ActionKeyInfo(Context activityContext, AttributeSet attr) {
|
||||||
TypedArray a = activityContext.obtainStyledAttributes(attr,
|
TypedArray a = activityContext.obtainStyledAttributes(attr,
|
||||||
com.android.internal.R.styleable.SearchableActionKey);
|
com.android.internal.R.styleable.SearchableActionKey);
|
||||||
|
|
||||||
@ -326,22 +347,19 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
com.android.internal.R.styleable.SearchableActionKey_keycode, 0);
|
com.android.internal.R.styleable.SearchableActionKey_keycode, 0);
|
||||||
mQueryActionMsg = a.getString(
|
mQueryActionMsg = a.getString(
|
||||||
com.android.internal.R.styleable.SearchableActionKey_queryActionMsg);
|
com.android.internal.R.styleable.SearchableActionKey_queryActionMsg);
|
||||||
if (DBG_INHIBIT_SUGGESTIONS == 0) {
|
mSuggestActionMsg = a.getString(
|
||||||
mSuggestActionMsg = a.getString(
|
com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg);
|
||||||
com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg);
|
mSuggestActionMsgColumn = a.getString(
|
||||||
mSuggestActionMsgColumn = a.getString(
|
com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn);
|
||||||
com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn);
|
|
||||||
}
|
|
||||||
a.recycle();
|
a.recycle();
|
||||||
|
|
||||||
// initialize any other fields
|
// sanity check.
|
||||||
mNext = next;
|
if (mKeyCode == 0) {
|
||||||
|
throw new IllegalArgumentException("No keycode.");
|
||||||
// sanity check. must have at least one action message, or invalidate the object.
|
} else if ((mQueryActionMsg == null) &&
|
||||||
if ((mQueryActionMsg == null) &&
|
|
||||||
(mSuggestActionMsg == null) &&
|
(mSuggestActionMsg == null) &&
|
||||||
(mSuggestActionMsgColumn == null)) {
|
(mSuggestActionMsgColumn == null)) {
|
||||||
mKeyCode = 0;
|
throw new IllegalArgumentException("No message information.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,14 +369,28 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
*
|
*
|
||||||
* @param in The Parcel containing the previously written ActionKeyInfo,
|
* @param in The Parcel containing the previously written ActionKeyInfo,
|
||||||
* positioned at the location in the buffer where it was written.
|
* positioned at the location in the buffer where it was written.
|
||||||
* @param next The value to place in mNext, creating a linked list
|
|
||||||
*/
|
*/
|
||||||
public ActionKeyInfo(Parcel in, ActionKeyInfo next) {
|
public ActionKeyInfo(Parcel in) {
|
||||||
mKeyCode = in.readInt();
|
mKeyCode = in.readInt();
|
||||||
mQueryActionMsg = in.readString();
|
mQueryActionMsg = in.readString();
|
||||||
mSuggestActionMsg = in.readString();
|
mSuggestActionMsg = in.readString();
|
||||||
mSuggestActionMsgColumn = in.readString();
|
mSuggestActionMsgColumn = in.readString();
|
||||||
mNext = next;
|
}
|
||||||
|
|
||||||
|
public int getKeyCode() {
|
||||||
|
return mKeyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQueryActionMsg() {
|
||||||
|
return mQueryActionMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSuggestActionMsg() {
|
||||||
|
return mSuggestActionMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSuggestActionMsgColumn() {
|
||||||
|
return mSuggestActionMsgColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
@ -380,14 +412,17 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
* @return Returns the ActionKeyInfo record, or null if none defined
|
* @return Returns the ActionKeyInfo record, or null if none defined
|
||||||
*/
|
*/
|
||||||
public ActionKeyInfo findActionKey(int keyCode) {
|
public ActionKeyInfo findActionKey(int keyCode) {
|
||||||
ActionKeyInfo info = mActionKeyList;
|
if (mActionKeys == null) {
|
||||||
while (info != null) {
|
return null;
|
||||||
if (info.mKeyCode == keyCode) {
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
info = info.mNext;
|
|
||||||
}
|
}
|
||||||
return null;
|
return mActionKeys.get(keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addActionKey(ActionKeyInfo keyInfo) {
|
||||||
|
if (mActionKeys == null) {
|
||||||
|
mActionKeys = new HashMap<Integer,ActionKeyInfo>();
|
||||||
|
}
|
||||||
|
mActionKeys.put(keyInfo.getKeyCode(), keyInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo) {
|
public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo) {
|
||||||
@ -401,14 +436,22 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
|
|
||||||
SearchableInfo searchable = getActivityMetaData(context, xml, cName);
|
SearchableInfo searchable = getActivityMetaData(context, xml, cName);
|
||||||
xml.close();
|
xml.close();
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(LOG_TAG, "Checked " + activityInfo.name
|
||||||
|
+ ",label=" + searchable.getLabelId()
|
||||||
|
+ ",icon=" + searchable.getIconId()
|
||||||
|
+ ",suggestAuthority=" + searchable.getSuggestAuthority()
|
||||||
|
+ ",target=" + searchable.getSearchActivity().getClassName()
|
||||||
|
+ ",global=" + searchable.shouldIncludeInGlobalSearch()
|
||||||
|
+ ",threshold=" + searchable.getSuggestThreshold());
|
||||||
|
}
|
||||||
return searchable;
|
return searchable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the metadata for a given activity
|
* Get the metadata for a given activity
|
||||||
*
|
*
|
||||||
* TODO: clean up where we return null vs. where we throw exceptions.
|
|
||||||
*
|
|
||||||
* @param context runtime context
|
* @param context runtime context
|
||||||
* @param xml XML parser for reading attributes
|
* @param xml XML parser for reading attributes
|
||||||
* @param cName The component name of the searchable activity
|
* @param cName The component name of the searchable activity
|
||||||
@ -429,9 +472,11 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) {
|
if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) {
|
||||||
AttributeSet attr = Xml.asAttributeSet(xml);
|
AttributeSet attr = Xml.asAttributeSet(xml);
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
result = new SearchableInfo(activityContext, attr, cName);
|
try {
|
||||||
// if the constructor returned a bad object, exit now.
|
result = new SearchableInfo(activityContext, attr, cName);
|
||||||
if (! result.mSearchable) {
|
} catch (IllegalArgumentException ex) {
|
||||||
|
Log.w(LOG_TAG, "Invalid searchable metadata for " +
|
||||||
|
cName.flattenToShortString() + ": " + ex.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -442,11 +487,12 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
AttributeSet attr = Xml.asAttributeSet(xml);
|
AttributeSet attr = Xml.asAttributeSet(xml);
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
ActionKeyInfo keyInfo = new ActionKeyInfo(activityContext, attr,
|
try {
|
||||||
result.mActionKeyList);
|
result.addActionKey(new ActionKeyInfo(activityContext, attr));
|
||||||
// only add to list if it is was useable
|
} catch (IllegalArgumentException ex) {
|
||||||
if (keyInfo.mKeyCode != 0) {
|
Log.w(LOG_TAG, "Invalid action key for " +
|
||||||
result.mActionKeyList = keyInfo;
|
cName.flattenToShortString() + ": " + ex.getMessage());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,9 +500,11 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
tagType = xml.next();
|
tagType = xml.next();
|
||||||
}
|
}
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
throw new RuntimeException(e);
|
Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
|
||||||
|
return null;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -610,7 +658,6 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
* positioned at the location in the buffer where it was written.
|
* positioned at the location in the buffer where it was written.
|
||||||
*/
|
*/
|
||||||
public SearchableInfo(Parcel in) {
|
public SearchableInfo(Parcel in) {
|
||||||
mSearchable = in.readInt() != 0;
|
|
||||||
mLabelId = in.readInt();
|
mLabelId = in.readInt();
|
||||||
mSearchActivity = ComponentName.readFromParcel(in);
|
mSearchActivity = ComponentName.readFromParcel(in);
|
||||||
mHintId = in.readInt();
|
mHintId = in.readInt();
|
||||||
@ -620,7 +667,6 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
mSearchInputType = in.readInt();
|
mSearchInputType = in.readInt();
|
||||||
mSearchImeOptions = in.readInt();
|
mSearchImeOptions = in.readInt();
|
||||||
mIncludeInGlobalSearch = in.readInt() != 0;
|
mIncludeInGlobalSearch = in.readInt() != 0;
|
||||||
setSearchModeFlags();
|
|
||||||
|
|
||||||
mSuggestAuthority = in.readString();
|
mSuggestAuthority = in.readString();
|
||||||
mSuggestPath = in.readString();
|
mSuggestPath = in.readString();
|
||||||
@ -629,10 +675,8 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
mSuggestIntentData = in.readString();
|
mSuggestIntentData = in.readString();
|
||||||
mSuggestThreshold = in.readInt();
|
mSuggestThreshold = in.readInt();
|
||||||
|
|
||||||
mActionKeyList = null;
|
for (int count = in.readInt(); count > 0; count--) {
|
||||||
int count = in.readInt();
|
addActionKey(new ActionKeyInfo(in));
|
||||||
while (count-- > 0) {
|
|
||||||
mActionKeyList = new ActionKeyInfo(in, mActionKeyList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mSuggestProviderPackage = in.readString();
|
mSuggestProviderPackage = in.readString();
|
||||||
@ -649,7 +693,6 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeInt(mSearchable ? 1 : 0);
|
|
||||||
dest.writeInt(mLabelId);
|
dest.writeInt(mLabelId);
|
||||||
mSearchActivity.writeToParcel(dest, flags);
|
mSearchActivity.writeToParcel(dest, flags);
|
||||||
dest.writeInt(mHintId);
|
dest.writeInt(mHintId);
|
||||||
@ -667,18 +710,13 @@ public final class SearchableInfo implements Parcelable {
|
|||||||
dest.writeString(mSuggestIntentData);
|
dest.writeString(mSuggestIntentData);
|
||||||
dest.writeInt(mSuggestThreshold);
|
dest.writeInt(mSuggestThreshold);
|
||||||
|
|
||||||
// This is usually a very short linked list so we'll just pre-count it
|
if (mActionKeys == null) {
|
||||||
ActionKeyInfo nextKeyInfo = mActionKeyList;
|
dest.writeInt(0);
|
||||||
int count = 0;
|
} else {
|
||||||
while (nextKeyInfo != null) {
|
dest.writeInt(mActionKeys.size());
|
||||||
++count;
|
for (ActionKeyInfo actionKey : mActionKeys.values()) {
|
||||||
nextKeyInfo = nextKeyInfo.mNext;
|
actionKey.writeToParcel(dest, flags);
|
||||||
}
|
}
|
||||||
dest.writeInt(count);
|
|
||||||
// Now write count of 'em
|
|
||||||
nextKeyInfo = mActionKeyList;
|
|
||||||
while (count-- > 0) {
|
|
||||||
nextKeyInfo.writeToParcel(dest, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.writeString(mSuggestProviderPackage);
|
dest.writeString(mSuggestProviderPackage);
|
||||||
|
@ -210,7 +210,7 @@ public class Searchables {
|
|||||||
SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
|
SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
|
||||||
if (searchable != null) {
|
if (searchable != null) {
|
||||||
newSearchablesList.add(searchable);
|
newSearchablesList.add(searchable);
|
||||||
newSearchablesMap.put(searchable.mSearchActivity, searchable);
|
newSearchablesMap.put(searchable.getSearchActivity(), searchable);
|
||||||
if (searchable.shouldIncludeInGlobalSearch()) {
|
if (searchable.shouldIncludeInGlobalSearch()) {
|
||||||
newSearchablesInGlobalSearchList.add(searchable);
|
newSearchablesInGlobalSearchList.add(searchable);
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,7 @@ public class SearchablesTest extends AndroidTestCase {
|
|||||||
|
|
||||||
SearchableInfo si = searchables.getSearchableInfo(thisActivity);
|
SearchableInfo si = searchables.getSearchableInfo(thisActivity);
|
||||||
assertNotNull(si);
|
assertNotNull(si);
|
||||||
assertTrue(si.mSearchable);
|
assertEquals(thisActivity, si.getSearchActivity());
|
||||||
assertEquals(thisActivity, si.mSearchActivity);
|
|
||||||
|
|
||||||
Context appContext = si.getActivityContext(mContext);
|
Context appContext = si.getActivityContext(mContext);
|
||||||
assertNotNull(appContext);
|
assertNotNull(appContext);
|
||||||
@ -194,10 +193,9 @@ public class SearchablesTest extends AndroidTestCase {
|
|||||||
|
|
||||||
private void checkSearchable(SearchableInfo si) {
|
private void checkSearchable(SearchableInfo si) {
|
||||||
assertNotNull(si);
|
assertNotNull(si);
|
||||||
assertTrue(si.mSearchable);
|
|
||||||
assertTrue(si.getLabelId() != 0); // This must be a useable string
|
assertTrue(si.getLabelId() != 0); // This must be a useable string
|
||||||
assertNotEmpty(si.mSearchActivity.getClassName());
|
assertNotEmpty(si.getSearchActivity().getClassName());
|
||||||
assertNotEmpty(si.mSearchActivity.getPackageName());
|
assertNotEmpty(si.getSearchActivity().getPackageName());
|
||||||
if (si.getSuggestAuthority() != null) {
|
if (si.getSuggestAuthority() != null) {
|
||||||
// The suggestion fields are largely optional, so we'll just confirm basic health
|
// The suggestion fields are largely optional, so we'll just confirm basic health
|
||||||
assertNotEmpty(si.getSuggestAuthority());
|
assertNotEmpty(si.getSuggestAuthority());
|
||||||
@ -210,12 +208,12 @@ public class SearchablesTest extends AndroidTestCase {
|
|||||||
/* For now, test the most common action key (CALL) */
|
/* For now, test the most common action key (CALL) */
|
||||||
ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
|
ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
|
||||||
if (ai != null) {
|
if (ai != null) {
|
||||||
assertEquals(ai.mKeyCode, KeyEvent.KEYCODE_CALL);
|
assertEquals(ai.getKeyCode(), KeyEvent.KEYCODE_CALL);
|
||||||
// one of these three fields must be non-null & non-empty
|
// one of these three fields must be non-null & non-empty
|
||||||
boolean m1 = (ai.mQueryActionMsg != null) && (ai.mQueryActionMsg.length() > 0);
|
boolean m1 = (ai.getQueryActionMsg() != null) && (ai.getQueryActionMsg().length() > 0);
|
||||||
boolean m2 = (ai.mSuggestActionMsg != null) && (ai.mSuggestActionMsg.length() > 0);
|
boolean m2 = (ai.getSuggestActionMsg() != null) && (ai.getSuggestActionMsg().length() > 0);
|
||||||
boolean m3 = (ai.mSuggestActionMsgColumn != null) &&
|
boolean m3 = (ai.getSuggestActionMsgColumn() != null) &&
|
||||||
(ai.mSuggestActionMsgColumn.length() > 0);
|
(ai.getSuggestActionMsgColumn().length() > 0);
|
||||||
assertTrue(m1 || m2 || m3);
|
assertTrue(m1 || m2 || m3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user