FileDocCategorySizeDatePackage
SearchDialog.javaAPI DocAndroid 5.1 API27025Thu Mar 12 22:22:10 GMT 2015android.app

SearchDialog

public class SearchDialog extends Dialog
Search dialog. This is controlled by the SearchManager and runs in the current foreground process.
hide

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
Constructors Summary
public SearchDialog(android.content.Context context, SearchManager searchManager)
Constructor - fires it up and makes it look like the search UI.

param
context Application Context we can use for system acess

        super(context, resolveDialogTheme(context));

        // Save voice intent for later queries/launching
        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);

        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    
Methods Summary
private voidcreateContentView()
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.IntentcreateIntent(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.

param
action Intent action.
param
data Intent data, or null.
param
extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or null.
param
query Intent query, or null.
param
actionKey The key code of the action key that was pressed, or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
param
actionMsg The message for the action key that was pressed, or null if none.
param
mode The search mode, one of the acceptable values for {@link SearchManager#SEARCH_MODE}, or {@code null}.
return
The intent.

        // 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 booleandoShow(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

return
true if search dialog showed, false if not

        // 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 voidhide()

        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 booleanisEmpty(android.widget.AutoCompleteTextView actv)

        return TextUtils.getTrimmedLength(actv.getText()) == 0;
    
static booleanisLandscapeMode(android.content.Context context)

        return context.getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE;
    
private booleanisOutOfBounds(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 voidlaunchIntent(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 voidlaunchQuerySearch()
Launch a search for the text in the query text field.

        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
    
protected voidlaunchQuerySearch(int actionKey, java.lang.String actionMsg)
Launch a search for the text in the query text field.

param
actionKey The key code of the action key that was pressed, or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
param
actionMsg The message for the action key that was pressed, or null if none.

        String query = mSearchAutoComplete.getText().toString();
        String action = Intent.ACTION_SEARCH;
        Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
        launchIntent(intent);
    
public voidonBackPressed()

        // 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 booleanonClosePressed()

        // Dismiss the dialog if close button is pressed when there's no query text
        if (isEmpty(mSearchAutoComplete)) {
            dismiss();
            return true;
        }

        return false;
    
public voidonConfigurationChanged()
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 voidonCreate(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 voidonRestoreInstanceState(android.os.Bundle savedInstanceState)
Restore the state of the dialog from a previously saved bundle.

param
savedInstanceState The state of the dialog previously saved by {@link #onSaveInstanceState()}.

        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.BundleonSaveInstanceState()
Save the minimal set of data necessary to recreate the search

return
A bundle with the state of the dialog, or {@code null} if the search dialog is not showing.

        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 voidonStart()

        super.onStart();

        // Register a listener for configuration change events.
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        getContext().registerReceiver(mConfChangeListener, filter);
    
public voidonStop()
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 booleanonTouchEvent(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 intresolveDialogTheme(android.content.Context context)


        
        TypedValue outValue = new TypedValue();
        context.getTheme().resolveAttribute(com.android.internal.R.attr.searchDialogTheme,
                outValue, true);
        return outValue.resourceId;
    
public voidsetListSelection(int index)
Sets the list item selection in the AutoCompleteTextView's ListView.

        mSearchAutoComplete.setListSelection(index);
    
private voidsetUserQuery(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 voidsetWorking(boolean working)
Sets the search dialog to the 'working' state, which shows a working spinner in the right hand size of the text field.

param
working true to show spinner, false to hide spinner

        mWorkingSpinner.setAlpha(working ? 255 : 0);
        mWorkingSpinner.setVisible(working, false);
        mWorkingSpinner.invalidateSelf();
    
public booleanshow(java.lang.String initialQuery, boolean selectInitialQuery, android.content.ComponentName componentName, android.os.Bundle appSearchData)
Set up the search dialog

return
true if search dialog launched, false if not

        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 booleanshow(android.content.ComponentName componentName, android.os.Bundle appSearchData)
Sets up the search dialog and shows it.

return
true if search dialog launched

        
        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 voidupdateSearchAppIcon()

        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 voidupdateSearchAutoComplete()
Updates the auto-complete text view.

        // we dismiss the entire dialog instead
        mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
        mSearchAutoComplete.setForceIgnoreOutsideTouch(false);
    
private voidupdateSearchBadge()
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 voidupdateUI()
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);
            }
        }