SearchView.java revision 9dcd2e58138ca4eb4b18f80b50e8979329e859d6
1818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes/* 2818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Copyright (C) 2013 The Android Open Source Project 3818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 4818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * you may not use this file except in compliance with the License. 6818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * You may obtain a copy of the License at 7818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 8818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 10818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Unless required by applicable law or agreed to in writing, software 11818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * See the License for the specific language governing permissions and 14818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * limitations under the License. 15818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 16818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 17818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banespackage android.support.v7.widget; 18818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 19818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.app.PendingIntent; 20818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.app.SearchManager; 21818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.app.SearchableInfo; 22818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.ActivityNotFoundException; 23818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.ComponentName; 24818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.Context; 25818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.Intent; 26818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.pm.PackageManager; 27818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.pm.ResolveInfo; 28818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.res.Configuration; 29818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.res.Resources; 30818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.content.res.TypedArray; 31818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.database.Cursor; 32818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.graphics.Rect; 33818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.graphics.drawable.Drawable; 34818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.net.Uri; 35818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.os.Build; 36818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.os.Bundle; 372e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banesimport android.os.ResultReceiver; 38818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.speech.RecognizerIntent; 39818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.support.v4.view.KeyEventCompat; 40818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.support.v4.widget.CursorAdapter; 41818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.support.v7.appcompat.R; 422e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banesimport android.support.v7.view.CollapsibleActionView; 43818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.Editable; 44818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.InputType; 45818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.Spannable; 46818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.SpannableStringBuilder; 47818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.TextUtils; 48818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.TextWatcher; 49818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.text.style.ImageSpan; 50818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.util.AttributeSet; 51818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.util.Log; 52818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.util.TypedValue; 53818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.KeyEvent; 54818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.LayoutInflater; 55818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.View; 56818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.ViewTreeObserver; 57818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.inputmethod.EditorInfo; 58818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.view.inputmethod.InputMethodManager; 59818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.AdapterView; 60818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.AdapterView.OnItemClickListener; 61818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.AdapterView.OnItemSelectedListener; 62818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.AutoCompleteTextView; 63818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.ImageView; 64818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.LinearLayout; 65818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.ListView; 66818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.TextView; 67818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport android.widget.TextView.OnEditorActionListener; 68818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 69818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport java.lang.reflect.Method; 70818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport java.util.WeakHashMap; 71818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 72818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banesimport static android.support.v7.widget.SuggestionsAdapter.getColumnString; 73818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 74818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes/** 75818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * A widget that provides a user interface for the user to enter a search query and submit a request 76818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * to a search provider. Shows a list of query suggestions or results, if available, and allows the 77818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * user to pick a suggestion or result to launch into. 78818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 799dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * <p class="note"><strong>Note:</strong> This class is included in the <a 809dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * href="{@docRoot}tools/extras/support-library.html">support library</a> for compatibility 819dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * with API level 7 and higher. If you're developing your app for API level 11 and higher 829dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * <em>only</em>, you should instead use the framework {@link android.widget.SearchView} class.</p> 839dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * 84818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <p> 859dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * When the SearchView is used in an {@link android.support.v7.app.ActionBar} 869dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * as an action view, it's collapsed by default, so you must provide an icon for the action. 87818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * </p> 88818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <p> 899dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * If you want the search field to always be visible, then call 909dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * {@link #setIconifiedByDefault(boolean) setIconifiedByDefault(false)}. 91818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * </p> 92818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 93818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <div class="special reference"> 94818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <h3>Developer Guides</h3> 95818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <p>For information about using {@code SearchView}, read the 969dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * <a href="{@docRoot}guide/topics/search/index.html">Search</a> API guide. 979dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * Additional information about action views is also available in the <<a 989dcd2e58138ca4eb4b18f80b50e8979329e859d6Scott Main * href="{@docRoot}guide/topics/ui/actionbar.html#ActionView">Action Bar</a> API guide</p> 99818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * </div> 100818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 101818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see android.support.v4.view.MenuItemCompat#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 102818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 103818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banespublic class SearchView extends LinearLayout implements CollapsibleActionView { 104818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 105818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private static final boolean DBG = false; 106818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private static final String LOG_TAG = "SearchView"; 107818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 108818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 109818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Private constant for removing the microphone in the keyboard. 110818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 111818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private static final String IME_OPTION_NO_MICROPHONE = "nm"; 112818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 113818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private OnQueryTextListener mOnQueryChangeListener; 114818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private OnCloseListener mOnCloseListener; 115818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View.OnFocusChangeListener mOnQueryTextFocusChangeListener; 116818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private OnSuggestionListener mOnSuggestionListener; 117818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private OnClickListener mOnSearchClickListener; 118818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 119818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mIconifiedByDefault; 120818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mIconified; 121818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private CursorAdapter mSuggestionsAdapter; 122818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mSearchButton; 123818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mSubmitButton; 124818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mSearchPlate; 125818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mSubmitArea; 126818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private ImageView mCloseButton; 127818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mSearchEditFrame; 128818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mVoiceButton; 129818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private SearchAutoComplete mQueryTextView; 130818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private View mDropDownAnchor; 131818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private ImageView mSearchHintIcon; 132818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mSubmitButtonEnabled; 133818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private CharSequence mQueryHint; 134818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mQueryRefinement; 135818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mClearingFocus; 136818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private int mMaxWidth; 137818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mVoiceButtonEnabled; 138818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private CharSequence mOldQueryText; 139818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private CharSequence mUserQuery; 140818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean mExpandedInActionView; 141818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private int mCollapsedImeOptions; 142818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 143818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private SearchableInfo mSearchable; 144818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Bundle mAppSearchData; 145818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 146818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes static final AutoCompleteTextViewReflector HIDDEN_METHOD_INVOKER = new AutoCompleteTextViewReflector(); 147818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 148818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /* 149818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * SearchView can be set expanded before the IME is ready to be shown during 150818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * initial UI setup. The show operation is asynchronous to account for this. 151818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 152818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Runnable mShowImeRunnable = new Runnable() { 153818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void run() { 154818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes InputMethodManager imm = (InputMethodManager) 155818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 156818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 157818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (imm != null) { 1582e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes HIDDEN_METHOD_INVOKER.showSoftInputUnchecked(imm, SearchView.this, 0); 159818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 160818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 161818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 162818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 163818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Runnable mUpdateDrawableStateRunnable = new Runnable() { 164818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void run() { 165818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateFocusedState(); 166818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 167818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 168818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 169818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Runnable mReleaseCursorRunnable = new Runnable() { 170818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void run() { 171818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) { 172818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSuggestionsAdapter.changeCursor(null); 173818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 174818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 175818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 176818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 177818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // For voice searching 178818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final Intent mVoiceWebSearchIntent; 179818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final Intent mVoiceAppSearchIntent; 180818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 181818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // A weak map of drawables we've gotten from other packages, so we don't load them 182818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // more than once. 183818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = 184818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes new WeakHashMap<String, Drawable.ConstantState>(); 185818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 186818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 187818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Callbacks for changes to the query text. 188818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 189818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public interface OnQueryTextListener { 190818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 191818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 192818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called when the user submits the query. This could be due to a key press on the 193818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * keyboard or due to pressing a submit button. 194818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * The listener can override the standard behavior by returning true 195818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * to indicate that it has handled the submit request. Otherwise return false to 196818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * let the SearchView handle the submission by launching any associated intent. 197818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 198818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param query the query text that is to be submitted 199818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 200818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the query has been handled by the listener, false to let the 201818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * SearchView perform the default action. 202818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 203818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean onQueryTextSubmit(String query); 204818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 205818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 206818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called when the query text is changed by the user. 207818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 208818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param newText the new content of the query text field. 209818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 210818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return false if the SearchView should perform the default action of showing any 211818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * suggestions if available, true if the action was handled by the listener. 212818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 213818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean onQueryTextChange(String newText); 214818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 215818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 216818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public interface OnCloseListener { 217818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 218818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 219818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * The user is attempting to close the SearchView. 220818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 221818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the listener wants to override the default behavior of clearing the 222818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * text field and dismissing it, false otherwise. 223818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 224818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean onClose(); 225818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 226818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 227818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 228818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Callback interface for selection events on suggestions. These callbacks 229818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}. 230818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 231818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public interface OnSuggestionListener { 232818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 233818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 234818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called when a suggestion was selected by navigating to it. 235818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param position the absolute position in the list of suggestions. 236818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 237818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the listener handles the event and wants to override the default 238818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * behavior of possibly rewriting the query based on the selected item, false otherwise. 239818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 240818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean onSuggestionSelect(int position); 241818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 242818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 243818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called when a suggestion was clicked. 244818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param position the absolute position of the clicked item in the list of suggestions. 245818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 246818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the listener handles the event and wants to override the default 247818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * behavior of launching any intent or submitting a search query specified on that item. 248818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Return false otherwise. 249818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 250818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean onSuggestionClick(int position); 251818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 252818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 253818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public SearchView(Context context) { 254818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes this(context, null); 255818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 256818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 257818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public SearchView(Context context, AttributeSet attrs) { 258818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super(context, attrs); 259818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 260818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes LayoutInflater inflater = (LayoutInflater) context 261818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 262818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes inflater.inflate(R.layout.abc_search_view, this, true); 263818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 264818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchButton = findViewById(R.id.search_button); 265818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text); 266818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setSearchView(this); 267818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 268818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchEditFrame = findViewById(R.id.search_edit_frame); 269818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchPlate = findViewById(R.id.search_plate); 270818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitArea = findViewById(R.id.submit_area); 271818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitButton = findViewById(R.id.search_go_btn); 272818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mCloseButton = (ImageView) findViewById(R.id.search_close_btn); 273818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceButton = findViewById(R.id.search_voice_btn); 274818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon); 275818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 276818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchButton.setOnClickListener(mOnClickListener); 277818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mCloseButton.setOnClickListener(mOnClickListener); 278818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitButton.setOnClickListener(mOnClickListener); 279818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceButton.setOnClickListener(mOnClickListener); 280818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnClickListener(mOnClickListener); 281818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 282818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.addTextChangedListener(mTextWatcher); 283818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnEditorActionListener(mOnEditorActionListener); 284818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnItemClickListener(mOnItemClickListener); 285818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener); 286818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnKeyListener(mTextKeyListener); 287818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Inform any listener of focus changes 288818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() { 289818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 290818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onFocusChange(View v, boolean hasFocus) { 291818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnQueryTextFocusChangeListener != null) { 292818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus); 293818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 294818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 295818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }); 296818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 297818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0); 298818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true)); 299818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int maxWidth = a.getDimensionPixelSize(R.styleable.SearchView_android_maxWidth, -1); 300818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (maxWidth != -1) { 301818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setMaxWidth(maxWidth); 302818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 303818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence queryHint = a.getText(R.styleable.SearchView_queryHint); 304818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (!TextUtils.isEmpty(queryHint)) { 305818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setQueryHint(queryHint); 306818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 307818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int imeOptions = a.getInt(R.styleable.SearchView_android_imeOptions, -1); 308818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (imeOptions != -1) { 309818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeOptions(imeOptions); 310818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 311818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int inputType = a.getInt(R.styleable.SearchView_android_inputType, -1); 312818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (inputType != -1) { 313818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setInputType(inputType); 314818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 315818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 316818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes a.recycle(); 317818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 318818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean focusable = true; 319818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 320818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0); 321818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes focusable = a.getBoolean(R.styleable.View_android_focusable, focusable); 322818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes a.recycle(); 323818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setFocusable(focusable); 324818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 325818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Save voice intent for later queries/launching 326818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 327818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 328818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 329818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 330818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 331818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 332818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 333818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 334818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor()); 335818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mDropDownAnchor != null) { 336818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 337818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes addOnLayoutChangeListenerToDropDownAnchorSDK11(); 338818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 339818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes addOnLayoutChangeListenerToDropDownAnchorBase(); 340818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 341818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 342818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 343818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(mIconifiedByDefault); 344818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateQueryHint(); 345818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 346818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 347818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void addOnLayoutChangeListenerToDropDownAnchorSDK11() { 348818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mDropDownAnchor.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 349818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 350818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onLayoutChange(View v, int left, int top, int right, int bottom, 351818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int oldLeft, int oldTop, int oldRight, int oldBottom) { 352818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes adjustDropDownSizeAndPosition(); 353818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 354818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }); 355818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 356818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 357818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void addOnLayoutChangeListenerToDropDownAnchorBase() { 358818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mDropDownAnchor.getViewTreeObserver() 359818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 360818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 361818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onGlobalLayout() { 362818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes adjustDropDownSizeAndPosition(); 363818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 364818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }); 365818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 366818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 367818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 368818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used 369818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * to display labels, hints, suggestions, create intents for launching search results screens 370818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * and controlling other affordances such as a voice button. 371818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 372818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific 373818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * activity or a global search provider. 374818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 375818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setSearchableInfo(SearchableInfo searchable) { 376818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchable = searchable; 377818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable != null) { 378818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateSearchAutoComplete(); 379818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateQueryHint(); 380818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 381818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Cache the voice search capability 382818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceButtonEnabled = hasVoiceSearch(); 383818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 384818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mVoiceButtonEnabled) { 385818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Disable the microphone on the keyboard, as a mic is displayed near the text box 386818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // TODO: use imeOptions to disable voice input when the new API will be available 387818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE); 388818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 389818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(isIconified()); 390818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 391818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 392818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 393818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the APP_DATA for legacy SearchDialog use. 394818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param appSearchData bundle provided by the app when launching the search dialog 395818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @hide 396818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 397818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setAppSearchData(Bundle appSearchData) { 398818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mAppSearchData = appSearchData; 399818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 400818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 401818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 402818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the IME options on the query text field. 403818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 404818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see TextView#setImeOptions(int) 405818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param imeOptions the options to set on the query text field 406818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 407818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_imeOptions 408818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 409818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setImeOptions(int imeOptions) { 410818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setImeOptions(imeOptions); 411818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 412818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 413818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 414818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the IME options set on the query text field. 415818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the ime options 416818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see TextView#setImeOptions(int) 417818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 418818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_imeOptions 419818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 420818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public int getImeOptions() { 421818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mQueryTextView.getImeOptions(); 422818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 423818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 424818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 425818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the input type on the query text field. 426818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 427818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see TextView#setInputType(int) 428818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param inputType the input type to set on the query text field 429818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 430818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_inputType 431818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 432818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setInputType(int inputType) { 433818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setInputType(inputType); 434818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 435818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 436818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 437818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the input type set on the query text field. 438818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the input type 439818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 440818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_inputType 441818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 442818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public int getInputType() { 443818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mQueryTextView.getInputType(); 444818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 445818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 446818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** @hide */ 447818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 448818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 449818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Don't accept focus if in the middle of clearing focus 450818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mClearingFocus) return false; 451818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Check if SearchView is focusable. 452818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (!isFocusable()) return false; 453818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If it is not iconified, then give the focus to the text field 454818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (!isIconified()) { 455818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect); 456818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (result) { 457818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(false); 458818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 459818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return result; 460818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 461818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return super.requestFocus(direction, previouslyFocusedRect); 462818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 463818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 464818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 465818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** @hide */ 466818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 467818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void clearFocus() { 468818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mClearingFocus = true; 469818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeVisibility(false); 470818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.clearFocus(); 471818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.clearFocus(); 472818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mClearingFocus = false; 473818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 474818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 475818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 476818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a listener for user actions within the SearchView. 477818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 478818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param listener the listener object that receives callbacks when the user performs 479818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * actions in the SearchView such as clicking on buttons or typing a query. 480818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 481818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setOnQueryTextListener(OnQueryTextListener listener) { 482818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnQueryChangeListener = listener; 483818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 484818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 485818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 486818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a listener to inform when the user closes the SearchView. 487818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 488818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param listener the listener to call when the user closes the SearchView. 489818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 490818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setOnCloseListener(OnCloseListener listener) { 491818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnCloseListener = listener; 492818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 493818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 494818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 495818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a listener to inform when the focus of the query text field changes. 496818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 497818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param listener the listener to inform of focus changes. 498818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 499818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) { 500818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnQueryTextFocusChangeListener = listener; 501818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 502818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 503818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 504818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a listener to inform when a suggestion is focused or clicked. 505818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 506818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param listener the listener to inform of suggestion selection events. 507818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 508818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setOnSuggestionListener(OnSuggestionListener listener) { 509818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnSuggestionListener = listener; 510818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 511818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 512818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 513818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a listener to inform when the search button is pressed. This is only 514818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * relevant when the text field is not visible by default. Calling {@link #setIconified 515818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * setIconified(false)} can also cause this listener to be informed. 516818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 517818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param listener the listener to inform when the search button is clicked or 518818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * the text field is programmatically de-iconified. 519818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 520818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setOnSearchClickListener(OnClickListener listener) { 521818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnSearchClickListener = listener; 522818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 523818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 524818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 525818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the query string currently in the text field. 526818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 527818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the query string 528818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 529818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public CharSequence getQuery() { 530818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mQueryTextView.getText(); 531818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 532818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 533818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 534818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets a query string in the text field and optionally submits the query as well. 535818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 536818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param query the query string. This replaces any query text already present in the 537818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * text field. 538818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param submit whether to submit the query right now or only update the contents of 539818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * text field. 540818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 541818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setQuery(CharSequence query, boolean submit) { 542818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setText(query); 543818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (query != null) { 544818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setSelection(mQueryTextView.length()); 545818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mUserQuery = query; 546818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 547818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 548818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If the query is not empty and submit is requested, submit the query 549818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (submit && !TextUtils.isEmpty(query)) { 550818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onSubmitQuery(); 551818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 552818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 553818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 554818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 555818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the hint text to display in the query text field. This overrides any hint specified 556818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * in the SearchableInfo. 557818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 558818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param hint the hint text to display 559818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 560818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_queryHint 561818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 562818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setQueryHint(CharSequence hint) { 563818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryHint = hint; 564818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateQueryHint(); 565818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 566818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 567818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 568818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Gets the hint text to display in the query text field. 569818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the query hint text, if specified, null otherwise. 570818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 571818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_queryHint 572818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 573818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public CharSequence getQueryHint() { 574818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mQueryHint != null) { 575818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mQueryHint; 576818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (mSearchable != null) { 577818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence hint = null; 578818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int hintId = mSearchable.getHintId(); 579818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (hintId != 0) { 580818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes hint = getContext().getString(hintId); 581818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 582818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return hint; 583818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 584818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return null; 585818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 586818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 587818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 588818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the default or resting state of the search field. If true, a single search icon is 589818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * shown by default and expands to show the text field and other buttons when pressed. Also, 590818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * if the default state is iconified, then it collapses to that state when the close button 591818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * is pressed. Changes to this property will take effect immediately. 592818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 593818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * <p>The default value is true.</p> 594818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 595818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param iconified whether the search field should be iconified by default 596818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 597818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_iconifiedByDefault 598818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 599818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setIconifiedByDefault(boolean iconified) { 600818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mIconifiedByDefault == iconified) return; 601818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mIconifiedByDefault = iconified; 602818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(iconified); 603818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateQueryHint(); 604818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 605818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 606818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 607818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the default iconified state of the search field. 608818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return 609818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 610818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_iconifiedByDefault 611818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 612818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean isIconfiedByDefault() { 613818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mIconifiedByDefault; 614818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 615818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 616818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 617818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is 618818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * a temporary state and does not override the default iconified state set by 619818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then 620818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * a false here will only be valid until the user closes the field. And if the default 621818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * state is expanded, then a true here will only clear the text field and not close it. 622818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 623818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param iconify a true value will collapse the SearchView to an icon, while a false will 624818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * expand it. 625818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 626818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setIconified(boolean iconify) { 627818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (iconify) { 628818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onCloseClicked(); 629818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 630818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onSearchClicked(); 631818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 632818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 633818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 634818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 635818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the current iconified state of the SearchView. 636818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 637818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the SearchView is currently iconified, false if the search field is 638818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * fully visible. 639818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 640818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean isIconified() { 641818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mIconified; 642818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 643818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 644818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 645818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Enables showing a submit button when the query is non-empty. In cases where the SearchView 646818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * is being used to filter the contents of the current activity and doesn't launch a separate 647818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * results activity, then the submit button should be disabled. 648818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 649818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param enabled true to show a submit button for submitting queries, false if a submit 650818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * button is not required. 651818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 652818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setSubmitButtonEnabled(boolean enabled) { 653818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitButtonEnabled = enabled; 654818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(isIconified()); 655818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 656818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 657818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 658818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns whether the submit button is enabled when necessary or never displayed. 659818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 660818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return whether the submit button is enabled automatically when necessary 661818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 662818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean isSubmitButtonEnabled() { 663818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mSubmitButtonEnabled; 664818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 665818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 666818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 667818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Specifies if a query refinement button should be displayed alongside each suggestion 668818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or if it should depend on the flags set in the individual items retrieved from the 669818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * suggestions provider. Clicking on the query refinement button will replace the text 670818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * in the query text field with the text from the suggestion. This flag only takes effect 671818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)} 672818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * and not when using a custom adapter. 673818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 674818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param enable true if all items should have a query refinement button, false if only 675818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * those items that have a query refinement flag set should have the button. 676818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 677818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see SearchManager#SUGGEST_COLUMN_FLAGS 678818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see SearchManager#FLAG_QUERY_REFINEMENT 679818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 680818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setQueryRefinementEnabled(boolean enable) { 681818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryRefinement = enable; 682818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSuggestionsAdapter instanceof SuggestionsAdapter) { 683818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( 684818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY); 685818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 686818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 687818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 688818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 689818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns whether query refinement is enabled for all items or only specific ones. 690818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if enabled for all items, false otherwise. 691818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 692818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean isQueryRefinementEnabled() { 693818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mQueryRefinement; 694818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 695818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 696818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 697818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * You can set a custom adapter if you wish. Otherwise the default adapter is used to 698818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * display the suggestions from the suggestions provider associated with the SearchableInfo. 699818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 700818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @see #setSearchableInfo(SearchableInfo) 701818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 702818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setSuggestionsAdapter(CursorAdapter adapter) { 703818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSuggestionsAdapter = adapter; 704818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 705818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setAdapter(mSuggestionsAdapter); 706818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 707818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 708818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 709818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns the adapter used for suggestions, if any. 710818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the suggestions adapter 711818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 712818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public CursorAdapter getSuggestionsAdapter() { 713818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mSuggestionsAdapter; 714818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 715818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 716818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 717818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Makes the view at most this many pixels wide 718818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 719818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_maxWidth 720818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 721818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setMaxWidth(int maxpixels) { 722818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mMaxWidth = maxpixels; 723818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 724818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes requestLayout(); 725818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 726818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 727818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 728818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Gets the specified maximum width in pixels, if set. Returns zero if 729818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * no maximum width was specified. 730818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return the maximum width of the view 731818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 732818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @attr ref android.R.styleable#SearchView_maxWidth 733818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 734818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public int getMaxWidth() { 735818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mMaxWidth; 736818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 737818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 738818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 739818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 740818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Let the standard measurements take effect in iconified state. 741818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (isIconified()) { 742818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 743818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return; 744818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 745818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 746818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int widthMode = MeasureSpec.getMode(widthMeasureSpec); 747818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int width = MeasureSpec.getSize(widthMeasureSpec); 748818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 749818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes switch (widthMode) { 750818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes case MeasureSpec.AT_MOST: 751818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If there is an upper limit, don't exceed maximum width (explicit or implicit) 752818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mMaxWidth > 0) { 753818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes width = Math.min(mMaxWidth, width); 754818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 755818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes width = Math.min(getPreferredWidth(), width); 756818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 757818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes break; 758818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes case MeasureSpec.EXACTLY: 759818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If an exact width is specified, still don't exceed any specified maximum width 760818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mMaxWidth > 0) { 761818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes width = Math.min(mMaxWidth, width); 762818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 763818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes break; 764818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes case MeasureSpec.UNSPECIFIED: 765818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Use maximum width, if specified, else preferred width 766818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth(); 767818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes break; 768818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 769818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes widthMode = MeasureSpec.EXACTLY; 770818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec); 771818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 772818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 773818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private int getPreferredWidth() { 774818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return getContext().getResources() 77507a07ce59efb770e9fb9ca53a0133e5e64a63bbcChris Banes .getDimensionPixelSize(R.dimen.abc_search_view_preferred_width); 776818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 777818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 778818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateViewsVisibility(final boolean collapsed) { 779818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mIconified = collapsed; 780818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Visibility of views that are visible when collapsed 781818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes final int visCollapsed = collapsed ? VISIBLE : GONE; 782818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Is there text in the query 783818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); 784818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 785818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchButton.setVisibility(visCollapsed); 786818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateSubmitButton(hasText); 787818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); 788818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); 789818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateCloseButton(); 790818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateVoiceButton(!hasText); 791818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateSubmitArea(); 792818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 793818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 794818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean hasVoiceSearch() { 795818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) { 796818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent testIntent = null; 797818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable.getVoiceSearchLaunchWebSearch()) { 798818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes testIntent = mVoiceWebSearchIntent; 799818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { 800818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes testIntent = mVoiceAppSearchIntent; 801818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 802818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (testIntent != null) { 803818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent, 804818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes PackageManager.MATCH_DEFAULT_ONLY); 805818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return ri != null; 806818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 807818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 808818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 809818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 810818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 811818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean isSubmitAreaEnabled() { 812818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified(); 813818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 814818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 815818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateSubmitButton(boolean hasText) { 816818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int visibility = GONE; 817818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus() 818818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes && (hasText || !mVoiceButtonEnabled)) { 819818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes visibility = VISIBLE; 820818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 821818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitButton.setVisibility(visibility); 822818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 823818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 824818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateSubmitArea() { 825818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int visibility = GONE; 826818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (isSubmitAreaEnabled() 827818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes && (mSubmitButton.getVisibility() == VISIBLE 828818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes || mVoiceButton.getVisibility() == VISIBLE)) { 829818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes visibility = VISIBLE; 830818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 831818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitArea.setVisibility(visibility); 832818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 833818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 834818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateCloseButton() { 835818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); 836818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Should we show the close button? It is not shown if there's no focus, 837818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // field is not iconified by default and there is no text in it. 838818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView); 839818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mCloseButton.setVisibility(showClose ? VISIBLE : GONE); 840818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET); 841818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 842818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 843818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void postUpdateFocusedState() { 844818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes post(mUpdateDrawableStateRunnable); 845818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 846818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 847818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateFocusedState() { 848818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean focused = mQueryTextView.hasFocus(); 849818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); 850818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); 851818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes invalidate(); 852818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 853818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 854818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 855818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes protected void onDetachedFromWindow() { 856818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes removeCallbacks(mUpdateDrawableStateRunnable); 857818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes post(mReleaseCursorRunnable); 858818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onDetachedFromWindow(); 859818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 860818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 861818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void setImeVisibility(final boolean visible) { 862818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (visible) { 863818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes post(mShowImeRunnable); 864818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 865818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes removeCallbacks(mShowImeRunnable); 866818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes InputMethodManager imm = (InputMethodManager) 867818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 868818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 869818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (imm != null) { 870818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes imm.hideSoftInputFromWindow(getWindowToken(), 0); 871818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 872818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 873818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 874818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 875818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 876818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called by the SuggestionsAdapter 877818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @hide 878818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 879818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /* package */void onQueryRefine(CharSequence queryText) { 880818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setQuery(queryText); 881818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 882818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 883818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final OnClickListener mOnClickListener = new OnClickListener() { 884818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 885818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onClick(View v) { 886818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (v == mSearchButton) { 887818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onSearchClicked(); 888818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (v == mCloseButton) { 889818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onCloseClicked(); 890818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (v == mSubmitButton) { 891818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onSubmitQuery(); 892818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (v == mVoiceButton) { 893818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onVoiceClicked(); 894818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (v == mQueryTextView) { 895818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes forceSuggestionQuery(); 896818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 897818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 898818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 899818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 900818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 901818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Handles the key down event for dealing with action keys. 902818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 903818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param keyCode This is the keycode of the typed key, and is the same value as 904818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * found in the KeyEvent parameter. 905818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param event The complete event record for the typed key 906818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 907818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if the event was handled here, or false if not. 908818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 909818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 910818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean onKeyDown(int keyCode, KeyEvent event) { 911818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable == null) { 912818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 913818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 914818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 915818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return super.onKeyDown(keyCode, event); 916818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 917818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 918818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 919818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * React to the user typing "enter" or other hardwired keys while typing in 920818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * the search box. This handles these special keys while the edit box has 921818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * focus. 922818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 923818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { 924818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean onKey(View v, int keyCode, KeyEvent event) { 925818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // guard against possible race conditions 926818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable == null) { 927818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 928818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 929818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 930818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (DBG) { 931818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: " 932818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes + mQueryTextView.getListSelection()); 933818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 934818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 935818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If a suggestion is selected, handle enter, search key, and action keys 936818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // as presses on the selected suggestion 937818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mQueryTextView.isPopupShowing() 938818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) { 939818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return onSuggestionsKey(v, keyCode, event); 940818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 941818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 942818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If there is text in the query box, handle enter, and action keys 943818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // The search key is handled by the dialog's onKeyDown(). 944818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (!mQueryTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) { 945818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (event.getAction() == KeyEvent.ACTION_UP) { 946818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (keyCode == KeyEvent.KEYCODE_ENTER) { 947818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes v.cancelLongPress(); 948818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 949818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Launch as a regular search. 950818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText() 951818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .toString()); 952818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 953818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 954818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 955818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 956818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 957818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 958818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 959818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 960818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 961818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * React to the user typing while in the suggestions list. First, check for 962818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * action keys. If not handled, try refocusing regular characters into the 963818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * EditText. 964818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 965818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { 966818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // guard against possible race conditions (late arrival after dismiss) 967818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable == null) { 968818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 969818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 970818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSuggestionsAdapter == null) { 971818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 972818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 973818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (event.getAction() == KeyEvent.ACTION_DOWN && KeyEventCompat.hasNoModifiers(event)) { 974818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // First, check for enter or search (both of which we'll treat as a 975818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // "click") 976818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH 977818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes || keyCode == KeyEvent.KEYCODE_TAB) { 978818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int position = mQueryTextView.getListSelection(); 979818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); 980818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 981818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 982818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Next, check for left/right moves, which we use to "return" the 983818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // user to the edit view 984818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 985818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // give "focus" to text editor, with cursor at the beginning if 986818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // left key, at end if right key 987818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView 988818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .length(); 989818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setSelection(selPoint); 990818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setListSelection(0); 991818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.clearListSelection(); 992818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes HIDDEN_METHOD_INVOKER.ensureImeVisible(mQueryTextView, true); 993818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 994818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 995818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 996818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 997818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Next, check for an "up and out" move 998818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) { 999818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // TODO: restoreUserQuery(); 1000818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // let ACTV complete the move 1001818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 1002818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1003818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1004818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 1005818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1006818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1007818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private int getSearchIconId() { 1008818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes TypedValue outValue = new TypedValue(); 1009818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().getTheme().resolveAttribute(R.attr.searchViewSearchIcon, outValue, true); 1010818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return outValue.resourceId; 1011818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1012818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1013818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private CharSequence getDecoratedHint(CharSequence hintText) { 1014818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If the field is always expanded, then don't add the search icon to the hint 1015818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (!mIconifiedByDefault) return hintText; 1016818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1017818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon 1018818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ssb.append(hintText); 1019818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId()); 1020818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int textSize = (int) (mQueryTextView.getTextSize() * 1.25); 1021818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes searchIcon.setBounds(0, 0, textSize, textSize); 1022818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1023818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return ssb; 1024818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1025818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1026818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateQueryHint() { 1027818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mQueryHint != null) { 1028818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setHint(getDecoratedHint(mQueryHint)); 1029818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (mSearchable != null) { 1030818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence hint = null; 1031818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int hintId = mSearchable.getHintId(); 1032818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (hintId != 0) { 1033818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes hint = getContext().getString(hintId); 1034818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1035818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (hint != null) { 1036818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setHint(getDecoratedHint(hint)); 1037818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1038818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 1039818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setHint(getDecoratedHint("")); 1040818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1041818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1042818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1043818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1044818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Updates the auto-complete text view. 1045818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1046818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateSearchAutoComplete() { 1047818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setThreshold(mSearchable.getSuggestThreshold()); 1048818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setImeOptions(mSearchable.getImeOptions()); 1049818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int inputType = mSearchable.getInputType(); 1050818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // We only touch this if the input type is set up for text (which it almost certainly 1051818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // should be, in the case of search!) 1052818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) { 1053818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // The existence of a suggestions authority is the proxy for "suggestions 1054818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // are available here" 1055818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 1056818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable.getSuggestAuthority() != null) { 1057818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 1058818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing 1059818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // auto-completion based on its own semantics, which it will present to the user 1060818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // as they type. This generally means that the input method should not show its 1061818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // own candidates, and the spell checker should not be in action. The text editor 1062818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // supplies its candidates by calling InputMethodManager.displayCompletions(), 1063818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // which in turn will call InputMethodSession.displayCompletions(). 1064818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; 1065818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1066818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1067818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setInputType(inputType); 1068818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSuggestionsAdapter != null) { 1069818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSuggestionsAdapter.changeCursor(null); 1070818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1071818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // attach the suggestions adapter, if suggestions are available 1072818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // The existence of a suggestions authority is the proxy for "suggestions available here" 1073818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable.getSuggestAuthority() != null) { 1074818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSuggestionsAdapter = new SuggestionsAdapter(getContext(), 1075818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes this, mSearchable, mOutsideDrawablesCache); 1076818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setAdapter(mSuggestionsAdapter); 1077818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( 1078818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryRefinement ? SuggestionsAdapter.REFINE_ALL 1079818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes : SuggestionsAdapter.REFINE_BY_ENTRY); 1080818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1081818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1082818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1083818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1084818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Update the visibility of the voice button. There are actually two voice search modes, 1085818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * either of which will activate the button. 1086818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param empty whether the search query text field is empty. If it is, then the other 1087818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * criteria apply to make the voice button visible. 1088818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1089818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void updateVoiceButton(boolean empty) { 1090818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int visibility = GONE; 1091818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mVoiceButtonEnabled && !isIconified() && empty) { 1092818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes visibility = VISIBLE; 1093818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSubmitButton.setVisibility(GONE); 1094818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1095818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mVoiceButton.setVisibility(visibility); 1096818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1097818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1098818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() { 1099818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1100818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1101818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Called when the input method default action key is pressed. 1102818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1103818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 1104818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onSubmitQuery(); 1105818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1106818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1107818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 1108818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1109818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void onTextChanged(CharSequence newText) { 1110818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence text = mQueryTextView.getText(); 1111818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mUserQuery = text; 1112818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes boolean hasText = !TextUtils.isEmpty(text); 1113818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateSubmitButton(hasText); 1114818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateVoiceButton(!hasText); 1115818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateCloseButton(); 1116818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateSubmitArea(); 1117818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) { 1118818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnQueryChangeListener.onQueryTextChange(newText.toString()); 1119818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1120818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOldQueryText = newText.toString(); 1121818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1122818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1123818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void onSubmitQuery() { 1124818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence query = mQueryTextView.getText(); 1125818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (query != null && TextUtils.getTrimmedLength(query) > 0) { 1126818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnQueryChangeListener == null 1127818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) { 1128818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable != null) { 1129818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString()); 1130818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeVisibility(false); 1131818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1132818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes dismissSuggestions(); 1133818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1134818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1135818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1136818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1137818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void dismissSuggestions() { 1138818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.dismissDropDown(); 1139818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1140818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1141818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void onCloseClicked() { 1142818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence text = mQueryTextView.getText(); 1143818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (TextUtils.isEmpty(text)) { 1144818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mIconifiedByDefault) { 1145818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If the app doesn't override the close behavior 1146818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnCloseListener == null || !mOnCloseListener.onClose()) { 1147818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // hide the keyboard and remove focus 1148818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes clearFocus(); 1149818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // collapse the search field 1150818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(true); 1151818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1152818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1153818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 1154818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setText(""); 1155818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.requestFocus(); 1156818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeVisibility(true); 1157818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1158818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1159818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1160818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1161818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void onSearchClicked() { 1162818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(false); 1163818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.requestFocus(); 1164818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeVisibility(true); 1165818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnSearchClickListener != null) { 1166818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mOnSearchClickListener.onClick(this); 1167818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1168818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1169818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1170818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void onVoiceClicked() { 1171818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // guard against possible race conditions 1172818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mSearchable == null) { 1173818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return; 1174818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1175818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes SearchableInfo searchable = mSearchable; 1176818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1177818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (searchable.getVoiceSearchLaunchWebSearch()) { 1178818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent, 1179818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes searchable); 1180818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().startActivity(webSearchIntent); 1181818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (searchable.getVoiceSearchLaunchRecognizer()) { 1182818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent, 1183818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes searchable); 1184818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().startActivity(appSearchIntent); 1185818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1186818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (ActivityNotFoundException e) { 1187818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Should not happen, since we check the availability of 1188818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // voice search before showing the button. But just in case... 1189818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Log.w(LOG_TAG, "Could not find voice search activity"); 1190818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1191818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1192818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1193818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes void onTextFocusChanged() { 1194818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(isIconified()); 1195818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Delayed update to make sure that the focus has settled down and window focus changes 1196818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // don't affect it. A synchronous update was not working. 1197818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes postUpdateFocusedState(); 1198818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mQueryTextView.hasFocus()) { 1199818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes forceSuggestionQuery(); 1200818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1201818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1202818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1203818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1204818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onWindowFocusChanged(boolean hasWindowFocus) { 1205818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onWindowFocusChanged(hasWindowFocus); 1206818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1207818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes postUpdateFocusedState(); 1208818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1209818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1210818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1211818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * {@inheritDoc} 1212818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1213818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1214818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onActionViewCollapsed() { 1215818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes clearFocus(); 1216818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes updateViewsVisibility(true); 1217818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setImeOptions(mCollapsedImeOptions); 1218818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mExpandedInActionView = false; 1219818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1220818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1221818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1222818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * {@inheritDoc} 1223818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1224818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1225818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onActionViewExpanded() { 1226818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mExpandedInActionView) return; 1227818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1228818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mExpandedInActionView = true; 1229818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mCollapsedImeOptions = mQueryTextView.getImeOptions(); 1230818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN); 1231818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setText(""); 1232818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setIconified(false); 1233818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1234818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1235818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1236818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void adjustDropDownSizeAndPosition() { 1237818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mDropDownAnchor.getWidth() > 1) { 1238818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Resources res = getContext().getResources(); 1239818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int anchorPadding = mSearchPlate.getPaddingLeft(); 1240818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Rect dropDownPadding = new Rect(); 1241818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int iconOffset = mIconifiedByDefault 124207a07ce59efb770e9fb9ca53a0133e5e64a63bbcChris Banes ? res.getDimensionPixelSize(R.dimen.abc_dropdownitem_icon_width) 124307a07ce59efb770e9fb9ca53a0133e5e64a63bbcChris Banes + res.getDimensionPixelSize(R.dimen.abc_dropdownitem_text_padding_left) 1244818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes : 0; 1245818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.getDropDownBackground().getPadding(dropDownPadding); 1246818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int offset = anchorPadding - (dropDownPadding.left + iconOffset); 1247818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setDropDownHorizontalOffset(offset); 1248818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes final int width = mDropDownAnchor.getWidth() + dropDownPadding.left 1249818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes + dropDownPadding.right + iconOffset - anchorPadding; 1250818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setDropDownWidth(width); 1251818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1252818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1253818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1254818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean onItemClicked(int position, int actionKey, String actionMsg) { 1255818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnSuggestionListener == null 1256818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes || !mOnSuggestionListener.onSuggestionClick(position)) { 1257818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); 1258818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setImeVisibility(false); 1259818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes dismissSuggestions(); 1260818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1261818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1262818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 1263818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1264818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1265818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean onItemSelected(int position) { 1266818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mOnSuggestionListener == null 1267818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes || !mOnSuggestionListener.onSuggestionSelect(position)) { 1268818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes rewriteQueryFromSuggestion(position); 1269818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1270818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1271818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 1272818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1273818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1274818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { 1275818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1276818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1277818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Implements OnItemClickListener 1278818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1279818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1280818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); 1281818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); 1282818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1283818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 1284818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1285818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() { 1286818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1287818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1288818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Implements OnItemSelectedListener 1289818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1290818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 1291818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); 1292818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes SearchView.this.onItemSelected(position); 1293818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1294818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1295818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1296818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Implements OnItemSelectedListener 1297818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1298818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onNothingSelected(AdapterView<?> parent) { 1299818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (DBG) 1300818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Log.d(LOG_TAG, "onNothingSelected()"); 1301818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1302818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 1303818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1304818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1305818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Query rewriting. 1306818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1307818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void rewriteQueryFromSuggestion(int position) { 1308818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence oldQuery = mQueryTextView.getText(); 1309818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Cursor c = mSuggestionsAdapter.getCursor(); 1310818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (c == null) { 1311818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return; 1312818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1313818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (c.moveToPosition(position)) { 1314818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Get the new query from the suggestion. 1315818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes CharSequence newQuery = mSuggestionsAdapter.convertToString(c); 1316818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (newQuery != null) { 1317818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // The suggestion rewrites the query. 1318818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Update the text field, without getting new suggestions. 1319818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setQuery(newQuery); 1320818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 1321818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // The suggestion does not rewrite the query, restore the user's query. 1322818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setQuery(oldQuery); 1323818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1324818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else { 1325818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // We got a bad position, restore the user's query. 1326818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes setQuery(oldQuery); 1327818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1328818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1329818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1330818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1331818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Launches an intent based on a suggestion. 1332818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 1333818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param position The index of the suggestion to create the intent from. 1334818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionKey The key code of the action key that was pressed, 1335818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1336818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionMsg The message for the action key that was pressed, 1337818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or <code>null</code> if none. 1338818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return true if a successful launch, false if could not (e.g. bad position). 1339818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1340818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean launchSuggestion(int position, int actionKey, String actionMsg) { 1341818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Cursor c = mSuggestionsAdapter.getCursor(); 1342818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if ((c != null) && c.moveToPosition(position)) { 1343818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1344818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); 1345818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1346818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // launch the intent 1347818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes launchIntent(intent); 1348818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1349818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1350818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1351818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return false; 1352818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1353818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1354818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1355818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Launches an intent, including any special intent handling. 1356818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1357818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void launchIntent(Intent intent) { 1358818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (intent == null) { 1359818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return; 1360818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1361818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1362818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If the intent was created from a suggestion, it will always have an explicit 1363818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // component here. 1364818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().startActivity(intent); 1365818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (RuntimeException ex) { 1366818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Log.e(LOG_TAG, "Failed launch activity: " + intent, ex); 1367818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1368818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1369818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1370818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1371818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Sets the text in the query box, without updating the suggestions. 1372818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1373818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void setQuery(CharSequence query) { 1374818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setText(query); 1375818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Move the cursor to the end 1376818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length()); 1377818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1378818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1379818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void launchQuerySearch(int actionKey, String actionMsg, String query) { 1380818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String action = Intent.ACTION_SEARCH; 1381818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent intent = createIntent(action, null, null, query, actionKey, actionMsg); 1382818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes getContext().startActivity(intent); 1383818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1384818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1385818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1386818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Constructs an intent from the given information and the search dialog state. 1387818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 1388818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param action Intent action. 1389818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param data Intent data, or <code>null</code>. 1390818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. 1391818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param query Intent query, or <code>null</code>. 1392818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionKey The key code of the action key that was pressed, 1393818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1394818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionMsg The message for the action key that was pressed, 1395818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or <code>null</code> if none. 1396818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return The intent. 1397818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1398818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Intent createIntent(String action, Uri data, String extraData, String query, 1399818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int actionKey, String actionMsg) { 1400818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Now build the Intent 1401818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent intent = new Intent(action); 1402818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1403818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // We need CLEAR_TOP to avoid reusing an old task that has other activities 1404818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // on top of the one we want. We don't want to do this in in-app search though, 1405818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // as it can be destructive to the activity stack. 1406818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (data != null) { 1407818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.setData(data); 1408818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1409818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.USER_QUERY, mUserQuery); 1410818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (query != null) { 1411818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.QUERY, query); 1412818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1413818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (extraData != null) { 1414818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); 1415818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1416818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mAppSearchData != null) { 1417818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.APP_DATA, mAppSearchData); 1418818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1419818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { 1420818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.ACTION_KEY, actionKey); 1421818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.putExtra(SearchManager.ACTION_MSG, actionMsg); 1422818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1423818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes intent.setComponent(mSearchable.getSearchActivity()); 1424818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return intent; 1425818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1426818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1427818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1428818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Create and return an Intent that can launch the voice search activity for web search. 1429818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1430818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) { 1431818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent voiceIntent = new Intent(baseIntent); 1432818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ComponentName searchActivity = searchable.getSearchActivity(); 1433818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null 1434818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes : searchActivity.flattenToShortString()); 1435818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return voiceIntent; 1436818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1437818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1438818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1439818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Create and return an Intent that can launch the voice search activity, perform a specific 1440818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * voice transcription, and forward the results to the searchable activity. 1441818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 1442818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param baseIntent The voice app search intent to start from 1443818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return A completely-configured intent ready to send to the voice search activity 1444818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1445818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) { 1446818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ComponentName searchActivity = searchable.getSearchActivity(); 1447818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1448818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // create the necessary intent to set up a search-and-forward operation 1449818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // in the voice search system. We have to keep the bundle separate, 1450818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // because it becomes immutable once it enters the PendingIntent 1451818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent queryIntent = new Intent(Intent.ACTION_SEARCH); 1452818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes queryIntent.setComponent(searchActivity); 1453818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent, 1454818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes PendingIntent.FLAG_ONE_SHOT); 1455818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1456818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Now set up the bundle that will be inserted into the pending intent 1457818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // when it's time to do the search. We always build it here (even if empty) 1458818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // because the voice search activity will always need to insert "QUERY" into 1459818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // it anyway. 1460818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Bundle queryExtras = new Bundle(); 1461818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (mAppSearchData != null) { 1462818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData); 1463818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1464818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1465818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Now build the intent to launch the voice search. Add all necessary 1466818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // extras to launch the voice recognizer, and then all the necessary extras 1467818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // to forward the results to the searchable activity 1468818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Intent voiceIntent = new Intent(baseIntent); 1469818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1470818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Add all of the configuration options supplied by the searchable's metadata 1471818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; 1472818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String prompt = null; 1473818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String language = null; 1474818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int maxResults = 1; 1475818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1476818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Resources resources = getResources(); 1477818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (searchable.getVoiceLanguageModeId() != 0) { 1478818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes languageModel = resources.getString(searchable.getVoiceLanguageModeId()); 1479818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1480818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (searchable.getVoicePromptTextId() != 0) { 1481818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes prompt = resources.getString(searchable.getVoicePromptTextId()); 1482818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1483818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (searchable.getVoiceLanguageId() != 0) { 1484818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes language = resources.getString(searchable.getVoiceLanguageId()); 1485818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1486818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (searchable.getVoiceMaxResults() != 0) { 1487818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes maxResults = searchable.getVoiceMaxResults(); 1488818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1489818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); 1490818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); 1491818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); 1492818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); 1493818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null 1494818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes : searchActivity.flattenToShortString()); 1495818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1496818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Add the values that configure forwarding the results 1497818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); 1498818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); 1499818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1500818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return voiceIntent; 1501818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1502818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1503818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1504818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * When a particular suggestion has been selected, perform the various lookups required 1505818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * to use the suggestion. This includes checking the cursor for suggestion-specific data, 1506818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * and/or falling back to the XML for defaults; It also creates REST style Uri data when 1507818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * the suggestion includes a data id. 1508818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * 1509818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param c The suggestions cursor, moved to the row of the user's selection 1510818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionKey The key code of the action key that was pressed, 1511818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1512818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @param actionMsg The message for the action key that was pressed, 1513818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * or <code>null</code> if none. 1514818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @return An intent for the suggestion at the cursor's position. 1515818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1516818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) { 1517818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1518818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // use specific action if supplied, or default action if supplied, or fixed default 1519818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); 1520818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1521818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (action == null) { 1522818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes action = mSearchable.getSuggestIntentAction(); 1523818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1524818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (action == null) { 1525818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes action = Intent.ACTION_SEARCH; 1526818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1527818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1528818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // use specific data if supplied, or default data if supplied 1529818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA); 1530818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (data == null) { 1531818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes data = mSearchable.getSuggestIntentData(); 1532818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1533818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // then, if an ID was provided, append it. 1534818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (data != null) { 1535818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); 1536818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (id != null) { 1537818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes data = data + "/" + Uri.encode(id); 1538818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1539818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1540818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Uri dataUri = (data == null) ? null : Uri.parse(data); 1541818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1542818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); 1543818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); 1544818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1545818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return createIntent(action, dataUri, extraData, query, actionKey, actionMsg); 1546818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (RuntimeException e ) { 1547818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int rowNum; 1548818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { // be really paranoid now 1549818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes rowNum = c.getPosition(); 1550818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (RuntimeException e2 ) { 1551818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes rowNum = -1; 1552818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1553818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum + 1554818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes " returned exception.", e); 1555818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return null; 1556818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1557818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1558818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1559818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private void forceSuggestionQuery() { 1560818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes HIDDEN_METHOD_INVOKER.doBeforeTextChanged(mQueryTextView); 1561818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes HIDDEN_METHOD_INVOKER.doAfterTextChanged(mQueryTextView); 1562818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1563818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1564818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes static boolean isLandscapeMode(Context context) { 1565818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return context.getResources().getConfiguration().orientation 1566818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes == Configuration.ORIENTATION_LANDSCAPE; 1567818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1568818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1569818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1570818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Callback to watch the text field for empty/non-empty 1571818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1572818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private TextWatcher mTextWatcher = new TextWatcher() { 1573818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1574818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void beforeTextChanged(CharSequence s, int start, int before, int after) { } 1575818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1576818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onTextChanged(CharSequence s, int start, 1577818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes int before, int after) { 1578818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes SearchView.this.onTextChanged(s); 1579818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1580818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1581818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void afterTextChanged(Editable s) { 1582818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1583818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes }; 1584818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1585818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1586818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Local subclass for AutoCompleteTextView. 1587818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * @hide 1588818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1589818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public static class SearchAutoComplete extends AutoCompleteTextView { 1590818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1591818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private int mThreshold; 1592818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private SearchView mSearchView; 1593818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1594818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public SearchAutoComplete(Context context) { 1595818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super(context); 1596818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mThreshold = getThreshold(); 1597818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1598818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1599818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public SearchAutoComplete(Context context, AttributeSet attrs) { 1600818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super(context, attrs); 1601818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mThreshold = getThreshold(); 1602818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1603818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1604818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { 1605818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super(context, attrs, defStyle); 1606818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mThreshold = getThreshold(); 1607818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1608818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1609818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes void setSearchView(SearchView searchView) { 1610818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchView = searchView; 1611818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1612818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1613818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1614818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void setThreshold(int threshold) { 1615818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.setThreshold(threshold); 1616818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mThreshold = threshold; 1617818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1618818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1619818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1620818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * Returns true if the text field is empty, or contains only whitespace. 1621818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1622818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private boolean isEmpty() { 1623818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return TextUtils.getTrimmedLength(getText()) == 0; 1624818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1625818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1626818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1627818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * We override this method to avoid replacing the query box text when a 1628818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * suggestion is clicked. 1629818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1630818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1631818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes protected void replaceText(CharSequence text) { 1632818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1633818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1634818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1635818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * We override this method to avoid an extra onItemClick being called on 1636818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * the drop-down's OnItemClickListener by 1637818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is 1638818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * clicked with the trackball. 1639818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1640818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1641818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void performCompletion() { 1642818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1643818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1644818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1645818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * We override this method to be sure and show the soft keyboard if 1646818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * appropriate when the TextView has focus. 1647818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1648818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1649818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public void onWindowFocusChanged(boolean hasWindowFocus) { 1650818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onWindowFocusChanged(hasWindowFocus); 1651818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1652818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) { 1653818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes InputMethodManager inputManager = (InputMethodManager) getContext() 1654818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .getSystemService(Context.INPUT_METHOD_SERVICE); 1655818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes inputManager.showSoftInput(this, 0); 1656818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // If in landscape mode, then make sure that 1657818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // the ime is in front of the dropdown. 1658818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (isLandscapeMode(getContext())) { 1659818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes HIDDEN_METHOD_INVOKER.ensureImeVisible(this, true); 1660818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1661818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1662818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1663818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1664818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1665818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 1666818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes super.onFocusChanged(focused, direction, previouslyFocusedRect); 1667818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchView.onTextFocusChanged(); 1668818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1669818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1670818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes /** 1671818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * We override this method so that we can allow a threshold of zero, 1672818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes * which ACTV does not. 1673818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes */ 1674818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1675818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean enoughToFilter() { 1676818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return mThreshold <= 0 || super.enoughToFilter(); 1677818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1678818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1679818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes @Override 1680818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes public boolean onKeyPreIme(int keyCode, KeyEvent event) { 1681818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (keyCode == KeyEvent.KEYCODE_BACK) { 1682818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // special case for the back key, we do not even try to send it 1683818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // to the drop down list but instead, consume it immediately 1684818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 1685818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes KeyEvent.DispatcherState state = getKeyDispatcherState(); 1686818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (state != null) { 1687818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes state.startTracking(event, this); 1688818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1689818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1690818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } else if (event.getAction() == KeyEvent.ACTION_UP) { 1691818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes KeyEvent.DispatcherState state = getKeyDispatcherState(); 1692818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (state != null) { 1693818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes state.handleUpEvent(event); 1694818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1695818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (event.isTracking() && !event.isCanceled()) { 1696818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchView.clearFocus(); 1697818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes mSearchView.setImeVisibility(false); 1698818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return true; 1699818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1700818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1701818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1702818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes return super.onKeyPreIme(keyCode, event); 1703818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1704818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1705818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1706818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private static class AutoCompleteTextViewReflector { 1707818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Method doBeforeTextChanged, doAfterTextChanged; 1708818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes private Method ensureImeVisible; 17092e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes private Method showSoftInputUnchecked; 1710818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1711818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes AutoCompleteTextViewReflector() { 1712818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1713818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doBeforeTextChanged = AutoCompleteTextView.class 1714818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .getDeclaredMethod("doBeforeTextChanged"); 1715818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doBeforeTextChanged.setAccessible(true); 1716818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (NoSuchMethodException e) { 1717818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Ah well. 1718818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1719818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1720818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doAfterTextChanged = AutoCompleteTextView.class 1721818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes .getDeclaredMethod("doAfterTextChanged"); 1722818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doAfterTextChanged.setAccessible(true); 1723818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (NoSuchMethodException e) { 1724818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Ah well. 1725818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1726818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1727818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ensureImeVisible = AutoCompleteTextView.class 17282e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes .getMethod("ensureImeVisible", boolean.class); 1729818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ensureImeVisible.setAccessible(true); 1730818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } catch (NoSuchMethodException e) { 1731818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes // Ah well. 1732818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 17332e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes try { 17342e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes showSoftInputUnchecked = InputMethodManager.class.getMethod( 17352e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes "showSoftInputUnchecked", int.class, ResultReceiver.class); 17362e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes showSoftInputUnchecked.setAccessible(true); 17372e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } catch (NoSuchMethodException e) { 17382e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes // Ah well. 17392e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } 1740818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1741818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1742818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes void doBeforeTextChanged(AutoCompleteTextView view) { 1743818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (doBeforeTextChanged != null) { 1744818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1745818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doBeforeTextChanged.invoke(view); 17462e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } catch (Exception e) { 1747818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1748818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1749818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1750818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1751818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes void doAfterTextChanged(AutoCompleteTextView view) { 1752818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (doAfterTextChanged != null) { 1753818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1754818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes doAfterTextChanged.invoke(view); 17552e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } catch (Exception e) { 1756818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1757818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1758818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1759818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes 1760818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes void ensureImeVisible(AutoCompleteTextView view, boolean visible) { 1761818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes if (ensureImeVisible != null) { 1762818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes try { 1763818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes ensureImeVisible.invoke(view, visible); 17642e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } catch (Exception e) { 1765818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1766818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1767818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 17682e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes 17692e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes void showSoftInputUnchecked(InputMethodManager imm, View view, int flags) { 17702e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes if (showSoftInputUnchecked != null) { 17712e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes try { 17722e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes showSoftInputUnchecked.invoke(imm, flags, null); 17732e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes return; 17742e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } catch (Exception e) { 17752e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } 17762e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } 17772e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes 17782e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes // Hidden method failed, call public version instead 17792e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes imm.showSoftInput(view, flags); 17802e21b5e22c320fd7e6af86a7cc05b4b11d7a0f64Chris Banes } 1781818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes } 1782818041428e0aca4943fe5adc0d54dad0d36a75f4Chris Banes}