Fields Summary |
---|
private static final boolean | DBG |
private static final String | LOG_TAG |
private static final String | INSTANCE_KEY_COMPONENT |
private static final String | INSTANCE_KEY_APPDATA |
private static final String | INSTANCE_KEY_USER_QUERY |
private static final String | IME_OPTION_NO_MICROPHONE |
private static final int | SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL |
private android.widget.TextView | mBadgeLabel |
private android.widget.ImageView | mAppIcon |
private android.widget.AutoCompleteTextView | mSearchAutoComplete |
private android.view.View | mSearchPlate |
private android.widget.SearchView | mSearchView |
private android.graphics.drawable.Drawable | mWorkingSpinner |
private android.view.View | mCloseSearch |
private SearchableInfo | mSearchable |
private android.content.ComponentName | mLaunchComponent |
private android.os.Bundle | mAppSearchData |
private android.content.Context | mActivityContext |
private final android.content.Intent | mVoiceWebSearchIntent |
private final android.content.Intent | mVoiceAppSearchIntent |
private String | mUserQuery |
private int | mSearchAutoCompleteImeOptions |
private android.content.BroadcastReceiver | mConfChangeListener |
private final SearchView.OnCloseListener | mOnCloseListener |
private final SearchView.OnQueryTextListener | mOnQueryChangeListener |
private final SearchView.OnSuggestionListener | mOnSuggestionSelectionListener |
Methods Summary |
---|
private void | createContentView()We recreate the dialog view each time it becomes visible so as to limit
the scope of any problems with the contained resources.
setContentView(com.android.internal.R.layout.search_bar);
// get the view elements for local access
SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar);
searchBar.setSearchDialog(this);
mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view);
mSearchView.setIconified(false);
mSearchView.setOnCloseListener(mOnCloseListener);
mSearchView.setOnQueryTextListener(mOnQueryChangeListener);
mSearchView.setOnSuggestionListener(mOnSuggestionSelectionListener);
mSearchView.onActionViewExpanded();
mCloseSearch = findViewById(com.android.internal.R.id.closeButton);
mCloseSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
// TODO: Move the badge logic to SearchView or move the badge to search_bar.xml
mBadgeLabel = (TextView) mSearchView.findViewById(com.android.internal.R.id.search_badge);
mSearchAutoComplete = (AutoCompleteTextView)
mSearchView.findViewById(com.android.internal.R.id.search_src_text);
mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate);
mWorkingSpinner = getContext().getDrawable(com.android.internal.R.drawable.search_spinner);
// TODO: Restore the spinner for slow suggestion lookups
// mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
// null, null, mWorkingSpinner, null);
setWorking(false);
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
// Additional adjustments to make Dialog work for Search
mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions();
|
private android.content.Intent | createIntent(java.lang.String action, android.net.Uri data, java.lang.String extraData, java.lang.String query, int actionKey, java.lang.String actionMsg)Constructs an intent from the given information and the search dialog state.
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
|
private boolean | doShow(java.lang.String initialQuery, boolean selectInitialQuery, android.content.ComponentName componentName, android.os.Bundle appSearchData)Does the rest of the work required to show the search dialog. Called by
{@link #show(String, boolean, ComponentName, Bundle)} and
// set up the searchable and show the dialog
if (!show(componentName, appSearchData)) {
return false;
}
// finally, load the user's initial text (which may trigger suggestions)
setUserQuery(initialQuery);
if (selectInitialQuery) {
mSearchAutoComplete.selectAll();
}
return true;
|
public void | hide()
if (!isShowing()) return;
// 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.hide();
|
private boolean | isEmpty(android.widget.AutoCompleteTextView actv)
return TextUtils.getTrimmedLength(actv.getText()) == 0;
|
static boolean | isLandscapeMode(android.content.Context context)
return context.getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
|
private boolean | isOutOfBounds(android.view.View v, android.view.MotionEvent event)
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
return (x < -slop) || (y < -slop)
|| (x > (v.getWidth()+slop))
|| (y > (v.getHeight()+slop));
|
private void | launchIntent(android.content.Intent intent)Launches an intent, including any special intent handling.
if (intent == null) {
return;
}
Log.d(LOG_TAG, "launching " + intent);
try {
// If the intent was created from a suggestion, it will always have an explicit
// component here.
getContext().startActivity(intent);
// If the search switches to a different activity,
// SearchDialogWrapper#performActivityResuming
// will handle hiding the dialog when the next activity starts, but for
// real in-app search, we still need to dismiss the dialog.
dismiss();
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
}
|
public void | launchQuerySearch()Launch a search for the text in the query text field.
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
|
protected void | launchQuerySearch(int actionKey, java.lang.String actionMsg)Launch a search for the text in the query text field.
String query = mSearchAutoComplete.getText().toString();
String action = Intent.ACTION_SEARCH;
Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
launchIntent(intent);
|
public void | onBackPressed()
// If the input method is covering the search dialog completely,
// e.g. in landscape mode with no hard keyboard, dismiss just the input method
InputMethodManager imm = (InputMethodManager)getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null && imm.isFullscreenMode() &&
imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) {
return;
}
// Close search dialog
cancel();
|
private boolean | onClosePressed()
// Dismiss the dialog if close button is pressed when there's no query text
if (isEmpty(mSearchAutoComplete)) {
dismiss();
return true;
}
return false;
|
public void | onConfigurationChanged()Called after resources have changed, e.g. after screen rotation or locale change.
if (mSearchable != null && isShowing()) {
// Redraw (resources may have changed)
updateSearchAppIcon();
updateSearchBadge();
if (isLandscapeMode(getContext())) {
mSearchAutoComplete.ensureImeVisible(true);
}
}
|
protected void | onCreate(android.os.Bundle savedInstanceState)Create the search dialog and any resources that are used for the
entire lifetime of the dialog.
super.onCreate(savedInstanceState);
Window theWindow = getWindow();
WindowManager.LayoutParams lp = theWindow.getAttributes();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
// taking up the whole window (even when transparent) is less than ideal,
// but necessary to show the popup window until the window manager supports
// having windows anchored by their parent but not clipped by them.
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
theWindow.setAttributes(lp);
// Touching outside of the search dialog will dismiss it
setCanceledOnTouchOutside(true);
|
public void | onRestoreInstanceState(android.os.Bundle savedInstanceState)Restore the state of the dialog from a previously saved bundle.
if (savedInstanceState == null) return;
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
// show the dialog.
if (!doShow(userQuery, false, launchComponent, appSearchData)) {
// for some reason, we couldn't re-instantiate
return;
}
|
public android.os.Bundle | onSaveInstanceState()Save the minimal set of data necessary to recreate the search
if (!isShowing()) return null;
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.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
return bundle;
|
public void | onStart()
super.onStart();
// Register a listener for configuration change events.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
getContext().registerReceiver(mConfChangeListener, filter);
|
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();
getContext().unregisterReceiver(mConfChangeListener);
// dump extra memory we're hanging on to
mLaunchComponent = null;
mAppSearchData = null;
mSearchable = null;
mUserQuery = null;
|
public boolean | onTouchEvent(android.view.MotionEvent event){@link Dialog#onTouchEvent(MotionEvent)} will cancel the dialog only when the
touch is outside the window. But the window includes space for the drop-down,
so we also cancel on taps outside the search bar when the drop-down is not showing.
// cancel if the drop-down is not showing and the touch event was outside the search plate
if (!mSearchAutoComplete.isPopupShowing() && isOutOfBounds(mSearchPlate, event)) {
if (DBG) Log.d(LOG_TAG, "Pop-up not showing and outside of search plate.");
cancel();
return true;
}
// Let Dialog handle events outside the window while the pop-up is showing.
return super.onTouchEvent(event);
|
static int | resolveDialogTheme(android.content.Context context)
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.searchDialogTheme,
outValue, true);
return outValue.resourceId;
|
public void | setListSelection(int index)Sets the list item selection in the AutoCompleteTextView's ListView.
mSearchAutoComplete.setListSelection(index);
|
private void | setUserQuery(java.lang.String query)Sets the text in the query box, updating the suggestions.
if (query == null) {
query = "";
}
mUserQuery = query;
mSearchAutoComplete.setText(query);
mSearchAutoComplete.setSelection(query.length());
|
public void | setWorking(boolean working)Sets the search dialog to the 'working' state, which shows a working spinner in the
right hand size of the text field.
mWorkingSpinner.setAlpha(working ? 255 : 0);
mWorkingSpinner.setVisible(working, false);
mWorkingSpinner.invalidateSelf();
|
public boolean | show(java.lang.String initialQuery, boolean selectInitialQuery, android.content.ComponentName componentName, android.os.Bundle appSearchData)Set up the search dialog
boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData);
if (success) {
// Display the drop down as soon as possible instead of waiting for the rest of the
// pending UI stuff to get done, so that things appear faster to the user.
mSearchAutoComplete.showDropDownAfterLayout();
}
return success;
|
private boolean | show(android.content.ComponentName componentName, android.os.Bundle appSearchData)Sets up the search dialog and shows it.
if (DBG) {
Log.d(LOG_TAG, "show(" + componentName + ", "
+ appSearchData + ")");
}
SearchManager searchManager = (SearchManager)
mContext.getSystemService(Context.SEARCH_SERVICE);
// Try to get the searchable info for the provided component.
mSearchable = searchManager.getSearchableInfo(componentName);
if (mSearchable == null) {
return false;
}
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
mActivityContext = mSearchable.getActivityContext(getContext());
// show the dialog. this will call onStart().
if (!isShowing()) {
// Recreate the search bar view every time the dialog is shown, to get rid
// of any bad state in the AutoCompleteTextView etc
createContentView();
mSearchView.setSearchableInfo(mSearchable);
mSearchView.setAppSearchData(mAppSearchData);
show();
}
updateUI();
return true;
|
private void | updateSearchAppIcon()
PackageManager pm = getContext().getPackageManager();
Drawable icon;
try {
ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
icon = pm.getApplicationIcon(info.applicationInfo);
if (DBG)
Log.d(LOG_TAG, "Using app-specific icon");
} catch (NameNotFoundException e) {
icon = pm.getDefaultActivityIcon();
Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
}
mAppIcon.setImageDrawable(icon);
mAppIcon.setVisibility(View.VISIBLE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), mSearchPlate.getPaddingBottom());
|
private void | updateSearchAutoComplete()Updates the auto-complete text view.
// we dismiss the entire dialog instead
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
mSearchAutoComplete.setForceIgnoreOutsideTouch(false);
|
private void | updateSearchBadge()Setup the search "Badge" if requested by mode flags.
// assume both hidden
int visibility = View.GONE;
Drawable icon = null;
CharSequence text = null;
// optionally show one or the other.
if (mSearchable.useBadgeIcon()) {
icon = mActivityContext.getDrawable(mSearchable.getIconId());
visibility = View.VISIBLE;
if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
} else if (mSearchable.useBadgeLabel()) {
text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
visibility = View.VISIBLE;
if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
}
mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
mBadgeLabel.setText(text);
mBadgeLabel.setVisibility(visibility);
|
private void | updateUI()Update the UI according to the info in the current value of {@link #mSearchable}.
if (mSearchable != null) {
mDecor.setVisibility(View.VISIBLE);
updateSearchAutoComplete();
updateSearchAppIcon();
updateSearchBadge();
// 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;
}
}
mSearchAutoComplete.setInputType(inputType);
mSearchAutoCompleteImeOptions = mSearchable.getImeOptions();
mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions);
// If the search dialog is going to show a voice search button, then don't let
// the soft keyboard display a microphone button if it would have otherwise.
if (mSearchable.getVoiceSearchEnabled()) {
mSearchAutoComplete.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
} else {
mSearchAutoComplete.setPrivateImeOptions(null);
}
}
|