Searchpublic class Search extends android.widget.LinearLayout implements android.widget.AdapterView.OnItemClickListener, android.view.View.OnLongClickListener, android.view.View.OnClickListener, android.view.View.OnKeyListener, android.text.TextWatcher, android.widget.AdapterView.OnItemSelectedListener
Fields Summary |
---|
private final String | TAG | private android.widget.AutoCompleteTextView | mSearchText | private android.widget.ImageButton | mGoButton | private android.widget.ImageButton | mVoiceButton | private android.view.View.OnLongClickListener | mLongClickListener | private SuggestionsAdapter | mSuggestionsAdapter | private android.server.search.SearchableInfo | mSearchable | private String | mSuggestionAction | private android.net.Uri | mSuggestionData | private String | mSuggestionQuery | private int | mItemSelected | private android.content.Intent | mVoiceSearchIntent | private android.graphics.Rect | mTempRect | private boolean | mRestoreFocus | private static float | mPaddingInsetCache of popup padding value after read from {@link Resources}. |
Constructors Summary |
---|
public Search(android.content.Context context, android.util.AttributeSet attrs)Used to inflate the Workspace from XML.
super(context, attrs);
mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
|
Methods Summary |
---|
public void | afterTextChanged(android.text.Editable s)Implements TextWatcher (for EditText)
| public void | beforeTextChanged(java.lang.CharSequence s, int start, int before, int after)Implements TextWatcher (for EditText)
| public void | clearQuery()In order to keep things simple, the external trigger will clear the query just before
focusing, so as to give you a fresh query. This way we eliminate any sources of
accidental query launching.
mSearchText.setText(null);
| private void | configureSearchableInfo()Read the searchable info from the search manager
ISearchManager sms;
SearchableInfo searchable;
sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
try {
// TODO null isn't the published use of this API, but it works when global=true
// TODO better implementation: defer all of this, let Home set it up
searchable = sms.getSearchableInfo(null, true);
} catch (RemoteException e) {
searchable = null;
}
if (searchable == null) {
// no suggestions so just get out (no need to continue)
return;
}
mSearchable = searchable;
| private void | configureSuggestions()Set up the suggestions provider mechanism
// get SearchableInfo
mSearchText.setOnItemClickListener(this);
mSearchText.setOnItemSelectedListener(this);
// attach the suggestions adapter
mSuggestionsAdapter = new SuggestionsAdapter(mContext,
com.android.internal.R.layout.search_dropdown_item_2line, null,
SuggestionsAdapter.TWO_LINE_FROM, SuggestionsAdapter.TWO_LINE_TO, mSearchable);
mSearchText.setAdapter(mSuggestionsAdapter);
| private void | configureVoiceSearchButton()If appropriate & available, configure voice search
Note: Because the home screen search widget is always web search, we only check for
getVoiceSearchLaunchWebSearch() modes. We don't support the alternate form of app-specific
voice search.
boolean voiceSearchVisible = false;
if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
// Enable the voice search button if there is an activity that can handle it
PackageManager pm = getContext().getPackageManager();
ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent,
PackageManager.MATCH_DEFAULT_ONLY);
voiceSearchVisible = ri != null;
}
// finally, set visible state of voice search button, as appropriate
mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
| SearchAutoCompleteTextView | getSearchInputField()
return (SearchAutoCompleteTextView) mSearchText;
| private boolean | launchSuggestion(android.widget.CursorAdapter ca, int position)Code to launch a suggestion query.
This is copied from SearchDialog.
if (ca != null) {
Cursor c = ca.getCursor();
if ((c != null) && c.moveToPosition(position)) {
setupSuggestionIntent(c, mSearchable);
SearchableInfo si = mSearchable;
String suggestionAction = mSuggestionAction;
Uri suggestionData = mSuggestionData;
String suggestionQuery = mSuggestionQuery;
sendLaunchIntent(suggestionAction, suggestionData, suggestionQuery, null,
KeyEvent.KEYCODE_UNKNOWN, null, si);
clearQuery();
return true;
}
}
return false;
| public void | onClick(android.view.View v)Implements OnClickListener (for button)
if (v == mGoButton) {
query();
} else if (v == mVoiceButton) {
try {
getContext().startActivity(mVoiceSearchIntent);
} catch (ActivityNotFoundException ex) {
// Should not happen, since we check the availability of
// voice search before showing the button. But just in case...
Log.w(TAG, "Could not find voice search activity");
}
}
| public void | onDetachedFromWindow()Remove internal cursor references when detaching from window which
prevents {@link Context} leaks.
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.changeCursor(null);
mSuggestionsAdapter = null;
}
| protected void | onFinishInflate()
super.onFinishInflate();
mSearchText = (AutoCompleteTextView) findViewById(R.id.input);
// TODO: This can be confusing when the user taps the text field to give the focus
// (it is not necessary but I ran into this issue several times myself)
// mTitleInput.setOnClickListener(this);
mSearchText.setOnKeyListener(this);
mSearchText.addTextChangedListener(this);
mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
mGoButton.setOnClickListener(this);
mVoiceButton.setOnClickListener(this);
mGoButton.setOnKeyListener(this);
mVoiceButton.setOnKeyListener(this);
mSearchText.setOnLongClickListener(this);
mGoButton.setOnLongClickListener(this);
mVoiceButton.setOnLongClickListener(this);
// disable the button since we start out w/empty input
mGoButton.setEnabled(false);
mGoButton.setFocusable(false);
configureSearchableInfo();
configureSuggestions();
configureVoiceSearchButton();
| public boolean | onInterceptTouchEvent(android.view.MotionEvent ev)
// Request focus unless the user tapped on the voice search button
final int x = (int) ev.getX();
final int y = (int) ev.getY();
final Rect frame = mTempRect;
mVoiceButton.getHitRect(frame);
if (!frame.contains(x, y)) {
requestFocusFromTouch();
}
return super.onInterceptTouchEvent(ev);
| public void | onItemClick(android.widget.AdapterView parent, android.view.View view, int position, long id)Implements OnItemClickListener
// Log.d(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(TAG, "onItemSelected() position " + position);
mItemSelected = position;
| public final boolean | onKey(android.view.View v, int keyCode, android.view.KeyEvent event)Implements OnKeyListener (for EditText and for button)
This plays some games with state in order to "soften" the strength of suggestions
presented. Suggestions should not be used unless the user specifically navigates to them
(or clicks them, in which case it's obvious). This is not the way that AutoCompleteTextBox
normally works.
if (v == mSearchText) {
boolean searchTrigger = (keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_SEARCH ||
keyCode == KeyEvent.KEYCODE_DPAD_CENTER);
if (event.getAction() == KeyEvent.ACTION_UP) {
// Log.d(TAG, "onKey() ACTION_UP isPopupShowing:" + mSearchText.isPopupShowing());
if (!mSearchText.isPopupShowing()) {
if (searchTrigger) {
query();
return true;
}
}
} else {
// Log.d(TAG, "onKey() ACTION_DOWN isPopupShowing:" + mSearchText.isPopupShowing() +
// " mItemSelected="+ mItemSelected);
if (searchTrigger && mItemSelected < 0) {
query();
return true;
}
}
} else if (v == mGoButton || v == mVoiceButton) {
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)) {
if (mSearchText.requestFocus()) {
handled = mSearchText.dispatchKeyEvent(event);
}
}
return handled;
}
return false;
| protected void | onLayout(boolean changed, int left, int top, int right, int bottom)When our size is changed, pass down adjusted width and offset values to
correctly center the {@link AutoCompleteTextView} popup and include our
padding.
super.onLayout(changed, left, top, right, bottom);
if (changed) {
if (mPaddingInset == -1) {
mPaddingInset = getResources().getDimension(R.dimen.search_widget_inset);
}
// Fill entire width of widget, minus padding inset
float paddedWidth = getWidth() - (mPaddingInset * 2);
float paddedOffset = -(mSearchText.getLeft() - mPaddingInset);
mSearchText.setDropDownWidth((int) paddedWidth);
mSearchText.setDropDownHorizontalOffset((int) paddedOffset);
}
| public boolean | onLongClick(android.view.View v)Implements OnLongClickListener (for button)
// Pretend that a long press on a child view is a long press on the search widget
if (mLongClickListener != null) {
return mLongClickListener.onLongClick(this);
}
return false;
| public void | onNothingSelected(android.widget.AdapterView parent)Implements OnItemSelectedListener
// Log.d(TAG, "onNothingSelected()");
mItemSelected = -1;
| public void | onTextChanged(java.lang.CharSequence s, int start, int before, int after)Implements TextWatcher (for EditText)
// enable the button if we have one or more non-space characters
boolean enabled = TextUtils.getTrimmedLength(mSearchText.getText()) != 0;
mGoButton.setEnabled(enabled);
mGoButton.setFocusable(enabled);
| public void | onWindowFocusChanged(boolean hasWindowFocus)
if (!hasWindowFocus && hasFocus()) {
mRestoreFocus = true;
}
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && mRestoreFocus) {
if (isInTouchMode()) {
final AutoCompleteTextView searchText = mSearchText;
searchText.setSelectAllOnFocus(false);
searchText.requestFocusFromTouch();
searchText.setSelectAllOnFocus(true);
}
mRestoreFocus = false;
}
| private void | query()
String query = mSearchText.getText().toString();
if (TextUtils.getTrimmedLength(mSearchText.getText()) == 0) {
return;
}
Bundle appData = new Bundle();
appData.putString(SearchManager.SOURCE, "launcher-widget");
sendLaunchIntent(Intent.ACTION_SEARCH, null, query, appData, 0, null, mSearchable);
clearQuery();
| 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.
This is copied from SearchDialog.
Intent launcher = new Intent(action);
launcher.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
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)
if (si != null) {
launcher.setComponent(si.mSearchActivity);
}
getContext().startActivity(launcher);
| public void | setOnLongClickListener(android.view.View.OnLongClickListener l)
super.setOnLongClickListener(l);
mLongClickListener = l;
| 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 and
mSuggestionQuery.
This is copied from SearchDialog.
try {
// use specific action if supplied, or default action if supplied, or fixed default
mSuggestionAction = null;
int column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
if (column >= 0) {
final String action = c.getString(column);
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;
column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
if (column >= 0) {
final String rowData = c.getString(column);
if (rowData != null) {
data = rowData;
}
}
if (data == null) {
data = si.getSuggestIntentData();
}
// then, if an ID was provided, append it.
if (data != null) {
column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
if (column >= 0) {
final String id = c.getString(column);
if (id != null) {
data = data + "/" + Uri.encode(id);
}
}
}
mSuggestionData = (data == null) ? null : Uri.parse(data);
mSuggestionQuery = null;
column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
if (column >= 0) {
final String query = c.getString(column);
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(TAG, "Search Suggestions cursor at row " + rowNum +
" returned exception" + e.toString());
}
|
|