SearchDialogpublic class SearchDialog extends Dialog implements android.widget.AdapterView.OnItemClickListener, android.widget.AdapterView.OnItemSelectedListenerIn-application-process implementation of Search Bar. This is still controlled by the
SearchManager, but it runs in the current activity's process to keep things lighter weight. |
Fields Summary |
---|
static final String | LOG_TAG | private static final int | DBG_LOG_TIMING | static final int | DBG_JAM_THREADING | android.content.IntentFilter | mCloseDialogsFilter | android.content.IntentFilter | mPackageFilter | private static final String | INSTANCE_KEY_COMPONENT | private static final String | INSTANCE_KEY_APPDATA | private static final String | INSTANCE_KEY_GLOBALSEARCH | private static final String | INSTANCE_KEY_DISPLAY_QUERY | private static final String | INSTANCE_KEY_DISPLAY_SEL_START | private static final String | INSTANCE_KEY_DISPLAY_SEL_END | private static final String | INSTANCE_KEY_USER_QUERY | private static final String | INSTANCE_KEY_SUGGESTION_QUERY | private static final String | INSTANCE_KEY_SELECTED_ELEMENT | private static final int | INSTANCE_SELECTED_BUTTON | private static final int | INSTANCE_SELECTED_QUERY | private android.widget.TextView | mBadgeLabel | private android.widget.AutoCompleteTextView | mSearchTextField | private android.widget.Button | mGoButton | private android.widget.ImageButton | mVoiceButton | private android.content.ComponentName | mLaunchComponent | private android.os.Bundle | mAppSearchData | private boolean | mGlobalSearchMode | private android.content.Context | mActivityContext | private android.server.search.SearchableInfo | mSearchable | private String | mUserQuery | private int | mUserQuerySelStart | private int | mUserQuerySelEnd | private boolean | mLeaveJammedQueryOnRefocus | private String | mPreviousSuggestionQuery | private int | mPresetSelection | private String | mSuggestionAction | private android.net.Uri | mSuggestionData | private String | mSuggestionQuery | private android.content.Intent | mVoiceWebSearchIntent | private android.content.Intent | mVoiceAppSearchIntent | private SuggestionsAdapter | mSuggestionsAdapter | private android.text.TextWatcher | mTextWatcherCallback to watch the textedit field for empty/non-empty | private static final String[] | ONE_LINE_FROM | private static final String[] | ONE_LINE_ICONS_FROM | private static final String[] | TWO_LINE_FROM | private static final String[] | TWO_LINE_ICONS_FROM | private static final int[] | ONE_LINE_TO | private static final int[] | ONE_LINE_ICONS_TO | private static final int[] | TWO_LINE_TO | private static final int[] | TWO_LINE_ICONS_TO | View.OnKeyListener | mButtonsKeyListenerReact to typing in the GO search button by refocusing to EditText.
Continue typing the query. | View.OnClickListener | mGoButtonClickListenerReact to a click in the GO button by launching a search. | View.OnClickListener | mVoiceButtonClickListenerReact to a click in the voice search button. | View.OnKeyListener | mTextKeyListenerReact to the user typing "enter" or other hardwired keys while typing in the search box.
This handles these special keys while the edit box has focus. | private android.content.BroadcastReceiver | mBroadcastReceiverThis is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent. It's an indication that
we should close ourselves immediately, in order to allow a higher-priority UI to take over
(e.g. phone call received). | private AtomicLong | mLastLogTimeFor debugging only, sample the millisecond clock and log it.
Uses AtomicLong so we can use in multiple threads |
Constructors Summary |
---|
public SearchDialog(android.content.Context context)Constructor - fires it up and makes it look like the search UI.
super(context, com.android.internal.R.style.Theme_SearchBar);
|
Methods Summary |
---|
public void | cancel()
// We made sure the IME was displayed, so also make sure it is closed
// when we go away.
InputMethodManager imm = (InputMethodManager)getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(
getWindow().getDecorView().getWindowToken(), 0);
}
super.cancel();
| private android.content.Intent | createVoiceAppSearchIntent(android.content.Intent baseIntent)Create and return an Intent that can launch the voice search activity, perform a specific
voice transcription, and forward the results to the searchable activity.
// create the necessary intent to set up a search-and-forward operation
// in the voice search system. We have to keep the bundle separate,
// because it becomes immutable once it enters the PendingIntent
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
queryIntent.setComponent(mSearchable.mSearchActivity);
PendingIntent pending = PendingIntent.getActivity(
getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
// Now set up the bundle that will be inserted into the pending intent
// when it's time to do the search. We always build it here (even if empty)
// because the voice search activity will always need to insert "QUERY" into
// it anyway.
Bundle queryExtras = new Bundle();
if (mAppSearchData != null) {
queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData);
}
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary extras
// to forward the results to the searchable activity
Intent voiceIntent = new Intent(baseIntent);
// Add all of the configuration options supplied by the searchable's metadata
String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
String prompt = null;
String language = null;
int maxResults = 1;
Resources resources = mActivityContext.getResources();
if (mSearchable.getVoiceLanguageModeId() != 0) {
languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
}
if (mSearchable.getVoicePromptTextId() != 0) {
prompt = resources.getString(mSearchable.getVoicePromptTextId());
}
if (mSearchable.getVoiceLanguageId() != 0) {
language = resources.getString(mSearchable.getVoiceLanguageId());
}
if (mSearchable.getVoiceMaxResults() != 0) {
maxResults = mSearchable.getVoiceMaxResults();
}
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
// Add the values that configure forwarding the results
voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
return voiceIntent;
| private void | dbgLogTiming(java.lang.String caller)
long millis = SystemClock.uptimeMillis();
long oldTime = mLastLogTime.getAndSet(millis);
long delta = millis - oldTime;
final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller;
Log.d(LOG_TAG,report);
| private boolean | doSuggestionsKey(android.view.View v, int keyCode, android.view.KeyEvent event)React to the user typing an action key while in the suggestions list
// Exit early in case of race condition
if (mSuggestionsAdapter == null) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (DBG_LOG_TIMING == 1) {
dbgLogTiming("doSuggestionsKey()");
}
// First, check for enter or search (both of which we'll treat as a "click")
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
int position = mSearchTextField.getListSelection();
return launchSuggestion(mSuggestionsAdapter, position);
}
// Next, check for left/right moves, which we use to "return" the user to the edit view
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
// give "focus" to text editor, but don't restore the user's original query
int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ?
0 : mSearchTextField.length();
mSearchTextField.setSelection(selPoint);
mSearchTextField.setListSelection(0);
mSearchTextField.clearListSelection();
return true;
}
// Next, check for an "up and out" move
if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchTextField.getListSelection()) {
jamSuggestionQuery(false, null, -1);
// let ACTV complete the move
return false;
}
// Next, check for an "action key"
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) &&
((actionKey.mSuggestActionMsg != null) ||
(actionKey.mSuggestActionMsgColumn != null))) {
// launch suggestion using action key column
int position = mSearchTextField.getListSelection();
if (position >= 0) {
Cursor c = mSuggestionsAdapter.getCursor();
if (c.moveToPosition(position)) {
final String actionMsg = getActionKeyMessage(c, actionKey);
if (actionMsg != null && (actionMsg.length() > 0)) {
// shut down search bar and launch the activity
// cache everything we need because dismiss releases mems
setupSuggestionIntent(c, mSearchable);
final String query = mSearchTextField.getText().toString();
final Bundle appData = mAppSearchData;
SearchableInfo si = mSearchable;
String suggestionAction = mSuggestionAction;
Uri suggestionData = mSuggestionData;
String suggestionQuery = mSuggestionQuery;
dismiss();
sendLaunchIntent(suggestionAction, suggestionData,
suggestionQuery, appData,
keyCode, actionMsg, si);
return true;
}
}
}
}
}
return false;
| private java.lang.String | getActionKeyMessage(android.database.Cursor c, SearchableInfo.ActionKeyInfo actionKey)For a given suggestion and a given cursor row, get the action message. If not provided
by the specific row/column, also check for a single definition (for the action key).
String result = null;
// check first in the cursor data, for a suggestion-specific message
final String column = actionKey.mSuggestActionMsgColumn;
if (column != null) {
try {
int colId = c.getColumnIndexOrThrow(column);
result = c.getString(colId);
} catch (RuntimeException e) {
// OK - result is already null
}
}
// If the cursor didn't give us a message, see if there's a single message defined
// for the actionkey (for all suggestions)
if (result == null) {
result = actionKey.mSuggestActionMsg;
}
return result;
| private static android.widget.CursorAdapter | getSuggestionsAdapter(android.widget.AdapterView adapterView)Safely retrieve the suggestions cursor adapter from the ListView
CursorAdapter result = null;
if (adapterView != null) {
Object ad = adapterView.getAdapter();
if (ad instanceof CursorAdapter) {
result = (CursorAdapter) ad;
} else if (ad instanceof WrapperListAdapter) {
result = (CursorAdapter) ((WrapperListAdapter)ad).getWrappedAdapter();
}
}
return result;
| private void | jamSuggestionQuery(boolean jamQuery, android.widget.AdapterView parent, int position)Set or reset the user query to follow the selections in the suggestions
// quick check against race conditions
if (mSearchable == null) {
return;
}
mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing
if (jamQuery) {
CursorAdapter ca = getSuggestionsAdapter(parent);
Cursor c = ca.getCursor();
if (c.moveToPosition(position)) {
setupSuggestionIntent(c, mSearchable);
String jamText = null;
// Simple heuristic for selecting text with which to rewrite the query.
if (mSuggestionQuery != null) {
jamText = mSuggestionQuery;
} else if (mSearchable.mQueryRewriteFromData && (mSuggestionData != null)) {
jamText = mSuggestionData.toString();
} else if (mSearchable.mQueryRewriteFromText) {
try {
int column = c.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_TEXT_1);
jamText = c.getString(column);
} catch (RuntimeException e) {
// no work here, jamText is null
}
}
if (jamText != null) {
mSearchTextField.setText(jamText);
/* mSearchTextField.selectAll(); */ // this didn't work anyway in the old UI
// TODO this is only needed in the model where we have a selection in the ACTV
// and in the dropdown at the same time.
mSearchTextField.setSelection(jamText.length());
}
}
} else {
// reset user query
mSearchTextField.setText(mUserQuery);
try {
mSearchTextField.setSelection(mUserQuerySelStart, mUserQuerySelEnd);
} catch (IndexOutOfBoundsException e) {
// In case of error, just select all
Log.e(LOG_TAG, "Caught IndexOutOfBoundsException while setting selection. " +
"start=" + mUserQuerySelStart + " end=" + mUserQuerySelEnd +
" text=\"" + mUserQuery + "\"");
mSearchTextField.selectAll();
}
}
// TODO because the new query is (not) processed in another thread, we can't just
// take away this flag (yet). The better solution here is going to require a new API
// in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
// mSuggestionsAdapter.setNonUserQuery(false);
| private void | launchQuerySearch(int actionKey, java.lang.String actionMsg)React to the user clicking the "GO" button. Hide the UI and launch a search.
final String query = mSearchTextField.getText().toString();
final Bundle appData = mAppSearchData;
final SearchableInfo si = mSearchable; // cache briefly (dismiss() nulls it)
dismiss();
sendLaunchIntent(Intent.ACTION_SEARCH, null, query, appData, actionKey, actionMsg, si);
| private boolean | launchSuggestion(android.widget.CursorAdapter ca, int position)Shared code for launching a query from a suggestion.
Cursor c = ca.getCursor();
if ((c != null) && c.moveToPosition(position)) {
setupSuggestionIntent(c, mSearchable);
final Bundle appData = mAppSearchData;
SearchableInfo si = mSearchable;
String suggestionAction = mSuggestionAction;
Uri suggestionData = mSuggestionData;
String suggestionQuery = mSuggestionQuery;
dismiss();
sendLaunchIntent(suggestionAction, suggestionData, suggestionQuery, appData,
KeyEvent.KEYCODE_UNKNOWN, null, si);
return true;
}
return false;
| public void | onConfigurationChanged(android.content.res.Configuration newConfig)Hook for updating layout on a rotation
if (isShowing()) {
// Redraw (resources may have changed)
updateSearchButton();
updateSearchBadge();
updateQueryHint();
}
| protected void | onCreate(android.os.Bundle savedInstanceState)We create the search dialog just once, and it stays around (hidden)
until activated by the user.
super.onCreate(savedInstanceState);
Window theWindow = getWindow();
theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL);
setContentView(com.android.internal.R.layout.search_bar);
theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
WindowManager.LayoutParams lp = theWindow.getAttributes();
lp.setTitle("Search Dialog");
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
theWindow.setAttributes(lp);
// get the view elements for local access
mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
mSearchTextField = (AutoCompleteTextView)
findViewById(com.android.internal.R.id.search_src_text);
mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
// attach listeners
mSearchTextField.addTextChangedListener(mTextWatcher);
mSearchTextField.setOnKeyListener(mTextKeyListener);
mGoButton.setOnClickListener(mGoButtonClickListener);
mGoButton.setOnKeyListener(mButtonsKeyListener);
mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
mVoiceButton.setOnKeyListener(mButtonsKeyListener);
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
// Additional adjustments to make Dialog work for Search
// Touching outside of the search dialog will dismiss it
setCanceledOnTouchOutside(true);
// Set up broadcast filters
mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mPackageFilter = new IntentFilter();
mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mPackageFilter.addDataScheme("package");
// Save voice intent for later queries/launching
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
| public void | onItemClick(android.widget.AdapterView parent, android.view.View view, int position, long id)Implements OnItemClickListener
// Log.d(LOG_TAG, "onItemClick() position " + position);
launchSuggestion(mSuggestionsAdapter, position);
| public void | onItemSelected(android.widget.AdapterView parent, android.view.View view, int position, long id)Implements OnItemSelectedListener
// Log.d(LOG_TAG, "onItemSelected() position " + position);
jamSuggestionQuery(true, parent, position);
| public boolean | onKeyDown(int keyCode, android.view.KeyEvent event)Dialog's OnKeyListener implements various search-specific functionality
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
cancel();
return true;
case KeyEvent.KEYCODE_SEARCH:
if (TextUtils.getTrimmedLength(mSearchTextField.getText()) != 0) {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
} else {
cancel();
}
return true;
default:
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
return true;
}
break;
}
return false;
| public void | onNothingSelected(android.widget.AdapterView parent)Implements OnItemSelectedListener
// Log.d(LOG_TAG, "onNothingSelected()");
| public void | onPackageListChange()The list of installed packages has just changed. This means that our current context
may no longer be valid. This would only happen if a package is installed/removed exactly
when the search bar is open. So for now we're just going to close the search
bar.
Anything fancier would require some checks to see if the user's context was still valid.
Which would be messier.
cancel();
| public void | onRestoreInstanceState(android.os.Bundle savedInstanceState)Restore the state of the dialog from a previously saved bundle.
// Get the launch info
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
// get the UI state
String displayQuery = savedInstanceState.getString(INSTANCE_KEY_DISPLAY_QUERY);
int querySelStart = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_START, -1);
int querySelEnd = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_END, -1);
String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
int selectedElement = savedInstanceState.getInt(INSTANCE_KEY_SELECTED_ELEMENT);
String suggestionQuery = savedInstanceState.getString(INSTANCE_KEY_SUGGESTION_QUERY);
// show the dialog. skip any show/hide animation, we want to go fast.
// send the text that actually generates the suggestions here; we'll replace the display
// text as necessary in a moment.
if (!show(suggestionQuery, false, launchComponent, appSearchData, globalSearch)) {
// for some reason, we couldn't re-instantiate
return;
}
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.setNonUserQuery(true);
}
mSearchTextField.setText(displayQuery);
// TODO because the new query is (not) processed in another thread, we can't just
// take away this flag (yet). The better solution here is going to require a new API
// in AutoCompleteTextView which allows us to change the text w/o changing the suggestions.
// mSuggestionsAdapter.setNonUserQuery(false);
// clean up the selection state
switch (selectedElement) {
case INSTANCE_SELECTED_BUTTON:
mGoButton.setEnabled(true);
mGoButton.setFocusable(true);
mGoButton.requestFocus();
break;
case INSTANCE_SELECTED_QUERY:
if (querySelStart >= 0 && querySelEnd >= 0) {
mSearchTextField.requestFocus();
mSearchTextField.setSelection(querySelStart, querySelEnd);
}
break;
default:
// defer selecting a list element until suggestion list appears
mPresetSelection = selectedElement;
// TODO mSearchTextField.setListSelection(selectedElement)
break;
}
| public android.os.Bundle | onSaveInstanceState()Save the minimal set of data necessary to recreate the search
Bundle bundle = new Bundle();
// setup info so I can recreate this particular search
bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode);
// UI state
bundle.putString(INSTANCE_KEY_DISPLAY_QUERY, mSearchTextField.getText().toString());
bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_START, mSearchTextField.getSelectionStart());
bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_END, mSearchTextField.getSelectionEnd());
bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
bundle.putString(INSTANCE_KEY_SUGGESTION_QUERY, mPreviousSuggestionQuery);
int selectedElement = INSTANCE_SELECTED_QUERY;
if (mGoButton.isFocused()) {
selectedElement = INSTANCE_SELECTED_BUTTON;
} else if (mSearchTextField.isPopupShowing()) {
selectedElement = 0; // TODO mSearchTextField.getListSelection() // 0..n
}
bundle.putInt(INSTANCE_KEY_SELECTED_ELEMENT, selectedElement);
return bundle;
| public void | onStop()The search dialog is being dismissed, so handle all of the local shutdown operations.
This function is designed to be idempotent so that dismiss() can be safely called at any time
(even if already closed) and more likely to really dump any memory. No leaks!
super.onStop();
setOnCancelListener(null);
setOnDismissListener(null);
// stop receiving broadcasts (throws exception if none registered)
try {
getContext().unregisterReceiver(mBroadcastReceiver);
} catch (RuntimeException e) {
// This is OK - it just means we didn't have any registered
}
// close any leftover cursor
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.changeCursor(null);
}
// dump extra memory we're hanging on to
mLaunchComponent = null;
mAppSearchData = null;
mSearchable = null;
mSuggestionAction = null;
mSuggestionData = null;
mSuggestionQuery = null;
mActivityContext = null;
mPreviousSuggestionQuery = null;
mUserQuery = null;
| private boolean | onSuggestionsKey(android.view.View v, int keyCode, android.view.KeyEvent event)React to the user typing while the suggestions are focused. First, check for action
keys. If not handled, try refocusing regular characters into the EditText. In this case,
replace the query text (start typing fresh text).
boolean handled = false;
// also guard against possible race conditions (late arrival after dismiss)
if (mSearchable != null) {
handled = doSuggestionsKey(v, keyCode, event);
}
return handled;
| private boolean | refocusingKeyListener(android.view.View v, int keyCode, android.view.KeyEvent event)Per UI design, we're going to "steer" any typed keystrokes back into the EditText
box, even if the user has navigated the focus to the dropdown or to the GO button.
boolean handled = false;
if (!event.isSystem() &&
(keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
(keyCode != KeyEvent.KEYCODE_DPAD_DOWN) &&
(keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
(keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
(keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
// restore focus and give key to EditText ...
// but don't replace the user's query
mLeaveJammedQueryOnRefocus = true;
if (mSearchTextField.requestFocus()) {
handled = mSearchTextField.dispatchKeyEvent(event);
}
mLeaveJammedQueryOnRefocus = false;
}
return handled;
| private void | sendLaunchIntent(java.lang.String action, android.net.Uri data, java.lang.String query, android.os.Bundle appData, int actionKey, java.lang.String actionMsg, android.server.search.SearchableInfo si)Assemble a search intent and send it.
Intent launcher = new Intent(action);
if (query != null) {
launcher.putExtra(SearchManager.QUERY, query);
}
if (data != null) {
launcher.setData(data);
}
if (appData != null) {
launcher.putExtra(SearchManager.APP_DATA, appData);
}
// add launch info (action key, etc.)
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
launcher.putExtra(SearchManager.ACTION_KEY, actionKey);
launcher.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
// attempt to enforce security requirement (no 3rd-party intents)
launcher.setComponent(si.mSearchActivity);
getContext().startActivity(launcher);
| private void | setupSearchableInfo()Use SearchableInfo record (from search manager service) to preconfigure the UI in various
ways.
if (mSearchable != null) {
mActivityContext = mSearchable.getActivityContext(getContext());
updateSearchButton();
updateSearchBadge();
updateQueryHint();
updateVoiceButton();
// In order to properly configure the input method (if one is being used), we
// need to let it know if we'll be providing suggestions. Although it would be
// difficult/expensive to know if every last detail has been configured properly, we
// can at least see if a suggestions provider has been configured, and use that
// as our trigger.
int inputType = mSearchable.getInputType();
// We only touch this if the input type is set up for text (which it almost certainly
// should be, in the case of search!)
if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
// The existence of a suggestions authority is the proxy for "suggestions
// are available here"
inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
if (mSearchable.getSuggestAuthority() != null) {
inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
}
}
mSearchTextField.setInputType(inputType);
mSearchTextField.setImeOptions(mSearchable.getImeOptions());
}
| void | setupSuggestionIntent(android.database.Cursor c, android.server.search.SearchableInfo si)When a particular suggestion has been selected, perform the various lookups required
to use the suggestion. This includes checking the cursor for suggestion-specific data,
and/or falling back to the XML for defaults; It also creates REST style Uri data when
the suggestion includes a data id.
NOTE: Return values are in member variables mSuggestionAction & mSuggestionData.
try {
// use specific action if supplied, or default action if supplied, or fixed default
mSuggestionAction = null;
int mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
if (mColumn >= 0) {
final String action = c.getString(mColumn);
if (action != null) {
mSuggestionAction = action;
}
}
if (mSuggestionAction == null) {
mSuggestionAction = si.getSuggestIntentAction();
}
if (mSuggestionAction == null) {
mSuggestionAction = Intent.ACTION_SEARCH;
}
// use specific data if supplied, or default data if supplied
String data = null;
mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
if (mColumn >= 0) {
final String rowData = c.getString(mColumn);
if (rowData != null) {
data = rowData;
}
}
if (data == null) {
data = si.getSuggestIntentData();
}
// then, if an ID was provided, append it.
if (data != null) {
mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
if (mColumn >= 0) {
final String id = c.getString(mColumn);
if (id != null) {
data = data + "/" + Uri.encode(id);
}
}
}
mSuggestionData = (data == null) ? null : Uri.parse(data);
mSuggestionQuery = null;
mColumn = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
if (mColumn >= 0) {
final String query = c.getString(mColumn);
if (query != null) {
mSuggestionQuery = query;
}
}
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
rowNum = c.getPosition();
} catch (RuntimeException e2 ) {
rowNum = -1;
}
Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
" returned exception" + e.toString());
}
| public boolean | show(java.lang.String initialQuery, boolean selectInitialQuery, android.content.ComponentName componentName, android.os.Bundle appSearchData, boolean globalSearch)Set up the search dialog
if (isShowing()) {
// race condition - already showing but not handling events yet.
// in this case, just discard the "show" request
return true;
}
// Get searchable info from search manager and use to set up other elements of UI
// Do this first so we can get out quickly if there's nothing to search
ISearchManager sms;
sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
try {
mSearchable = sms.getSearchableInfo(componentName, globalSearch);
} catch (RemoteException e) {
mSearchable = null;
}
if (mSearchable == null) {
// unfortunately, we can't log here. it would be logspam every time the user
// clicks the "search" key on a non-search app
return false;
}
// OK, we're going to show ourselves
super.show();
setupSearchableInfo();
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
mGlobalSearchMode = globalSearch;
// receive broadcasts
getContext().registerReceiver(mBroadcastReceiver, mCloseDialogsFilter);
getContext().registerReceiver(mBroadcastReceiver, mPackageFilter);
// configure the autocomplete aspects of the input box
mSearchTextField.setOnItemClickListener(this);
mSearchTextField.setOnItemSelectedListener(this);
// This conversion is necessary to force a preload of the EditText and thus force
// suggestions to be presented (even for an empty query)
if (initialQuery == null) {
initialQuery = ""; // This forces the preload to happen, triggering suggestions
}
// attach the suggestions adapter, if suggestions are available
// The existence of a suggestions authority is the proxy for "suggestions available here"
if (mSearchable.getSuggestAuthority() == null) {
mSuggestionsAdapter = null;
mSearchTextField.setAdapter(mSuggestionsAdapter);
mSearchTextField.setText(initialQuery);
} else {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable,
mSearchTextField);
mSearchTextField.setAdapter(mSuggestionsAdapter);
// finally, load the user's initial text (which may trigger suggestions)
mSuggestionsAdapter.setNonUserQuery(false);
mSearchTextField.setText(initialQuery);
}
if (selectInitialQuery) {
mSearchTextField.selectAll();
} else {
mSearchTextField.setSelection(initialQuery.length());
}
return true;
| public void | show()The default show() for this Dialog is not supported.
return;
| private void | updateQueryHint()Update the hint in the query text field.
if (isShowing()) {
String hint = null;
if (mSearchable != null) {
int hintId = mSearchable.getHintId();
if (hintId != 0) {
hint = mActivityContext.getString(hintId);
}
}
mSearchTextField.setHint(hint);
}
| private void | updateSearchBadge()Setup the search "Badge" if request by mode flags.
// assume both hidden
int visibility = View.GONE;
Drawable icon = null;
String text = null;
// optionally show one or the other.
if (mSearchable.mBadgeIcon) {
icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
visibility = View.VISIBLE;
} else if (mSearchable.mBadgeLabel) {
text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
visibility = View.VISIBLE;
}
mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
mBadgeLabel.setText(text);
mBadgeLabel.setVisibility(visibility);
| private void | updateSearchButton()Update the text in the search button. Note: This is deprecated functionality, for
1.0 compatibility only.
String textLabel = null;
Drawable iconLabel = null;
int textId = mSearchable.getSearchButtonText();
if (textId != 0) {
textLabel = mActivityContext.getResources().getString(textId);
} else {
iconLabel = getContext().getResources().
getDrawable(com.android.internal.R.drawable.ic_btn_search);
}
mGoButton.setText(textLabel);
mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null);
| private void | updateVoiceButton()Update the visibility of the voice button. There are actually two voice search modes,
either of which will activate the button.
int visibility = View.GONE;
if (mSearchable.getVoiceSearchEnabled()) {
Intent testIntent = null;
if (mSearchable.getVoiceSearchLaunchWebSearch()) {
testIntent = mVoiceWebSearchIntent;
} else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
testIntent = mVoiceAppSearchIntent;
}
if (testIntent != null) {
ResolveInfo ri = getContext().getPackageManager().
resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (ri != null) {
visibility = View.VISIBLE;
}
}
}
mVoiceButton.setVisibility(visibility);
| private void | updateWidgetState()Enable/Disable the cancel button based on edit text state (any text?)
// enable the button if we have one or more non-space characters
boolean enabled =
TextUtils.getTrimmedLength(mSearchTextField.getText()) != 0;
mGoButton.setEnabled(enabled);
mGoButton.setFocusable(enabled);
|
|