1733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani/* 2733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Copyright (C) 2010 The Android Open Source Project 3733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 4733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Licensed under the Apache License, Version 2.0 (the "License"); 5733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * you may not use this file except in compliance with the License. 6733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * You may obtain a copy of the License at 7733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 8733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * http://www.apache.org/licenses/LICENSE-2.0 9733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 10733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Unless required by applicable law or agreed to in writing, software 11733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * distributed under the License is distributed on an "AS IS" BASIS, 12733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * See the License for the specific language governing permissions and 14733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * limitations under the License. 15733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 16733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 17733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasanipackage android.widget; 18733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 19733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport static android.widget.SuggestionsAdapter.getColumnString; 20733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 21ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.app.PendingIntent; 22733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.app.SearchManager; 23733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.app.SearchableInfo; 24ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.content.ActivityNotFoundException; 25ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.content.ComponentName; 26733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.content.Context; 27733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.content.Intent; 28ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.content.pm.PackageManager; 29ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.content.pm.ResolveInfo; 30968ec938399033d280b1648123104ac567f2a093Amith Yamasaniimport android.content.res.Configuration; 31ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.content.res.Resources; 32733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.content.res.TypedArray; 33733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.database.Cursor; 346a81b8271d2edc1a87a3f50e8146f095be998629repo syncimport android.graphics.Rect; 35733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.graphics.drawable.Drawable; 36733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.net.Uri; 37ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.os.Bundle; 38ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasaniimport android.speech.RecognizerIntent; 39733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.text.Editable; 405607a3827172ff40196380d846128e892bedc118Amith Yamasaniimport android.text.InputType; 41b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasaniimport android.text.Spannable; 42b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasaniimport android.text.SpannableStringBuilder; 43733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.text.TextUtils; 44733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.text.TextWatcher; 45b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasaniimport android.text.style.ImageSpan; 46733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.util.AttributeSet; 47733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.util.Log; 48b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasaniimport android.util.TypedValue; 49763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasaniimport android.view.CollapsibleActionView; 50733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.view.KeyEvent; 51733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.view.LayoutInflater; 52733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.view.View; 538a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent; 548a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo; 555607a3827172ff40196380d846128e892bedc118Amith Yamasaniimport android.view.inputmethod.EditorInfo; 56733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.view.inputmethod.InputMethodManager; 57733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.widget.AdapterView.OnItemClickListener; 58733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.widget.AdapterView.OnItemSelectedListener; 59733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport android.widget.TextView.OnEditorActionListener; 60733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 61b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasaniimport com.android.internal.R; 62b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 63733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasaniimport java.util.WeakHashMap; 64733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 65733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani/** 66763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * A widget that provides a user interface for the user to enter a search query and submit a request 67763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * to a search provider. Shows a list of query suggestions or results, if available, and allows the 68763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * user to pick a suggestion or result to launch into. 695931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * 70763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * <p> 71763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * When the SearchView is used in an ActionBar as an action view for a collapsible menu item, it 72763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * needs to be set to iconified by default using {@link #setIconifiedByDefault(boolean) 73763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * setIconifiedByDefault(true)}. This is the default, so nothing needs to be done. 74763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * </p> 75763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * <p> 76763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * If you want the search field to always be visible, then call setIconifiedByDefault(false). 77763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * </p> 785931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * 793aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <div class="special reference"> 803aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <h3>Developer Guides</h3> 813aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <p>For information about using {@code SearchView}, read the 823aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <a href="{@docRoot}guide/topics/search/index.html">Search</a> developer guide.</p> 833aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * </div> 84763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * 85763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 865931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * @attr ref android.R.styleable#SearchView_iconifiedByDefault 875607a3827172ff40196380d846128e892bedc118Amith Yamasani * @attr ref android.R.styleable#SearchView_imeOptions 885607a3827172ff40196380d846128e892bedc118Amith Yamasani * @attr ref android.R.styleable#SearchView_inputType 895931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * @attr ref android.R.styleable#SearchView_maxWidth 90abdf0d533c292709e51cee2c1213d5e44baca963Scott Main * @attr ref android.R.styleable#SearchView_queryHint 91733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 92763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasanipublic class SearchView extends LinearLayout implements CollapsibleActionView { 93733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 94733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private static final boolean DBG = false; 95733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private static final String LOG_TAG = "SearchView"; 96733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 97535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin /** 98535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin * Private constant for removing the microphone in the keyboard. 99535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin */ 100535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin private static final String IME_OPTION_NO_MICROPHONE = "nm"; 101535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin 10201f21354654a315ea2a2a1668bbda50645123d4aAdam Powell private OnQueryTextListener mOnQueryChangeListener; 103733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private OnCloseListener mOnCloseListener; 1040594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani private OnFocusChangeListener mOnQueryTextFocusChangeListener; 10501f21354654a315ea2a2a1668bbda50645123d4aAdam Powell private OnSuggestionListener mOnSuggestionListener; 106483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani private OnClickListener mOnSearchClickListener; 107733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 108733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private boolean mIconifiedByDefault; 1099322775014432ed6c87c864e98fe482f879ff233Amith Yamasani private boolean mIconified; 110733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private CursorAdapter mSuggestionsAdapter; 111733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private View mSearchButton; 112733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private View mSubmitButton; 11379f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani private View mSearchPlate; 1149b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private View mSubmitArea; 1154aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani private ImageView mCloseButton; 116733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private View mSearchEditFrame; 117ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private View mVoiceButton; 118968ec938399033d280b1648123104ac567f2a093Amith Yamasani private SearchAutoComplete mQueryTextView; 119b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani private View mDropDownAnchor; 120b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani private ImageView mSearchHintIcon; 121733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private boolean mSubmitButtonEnabled; 122733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private CharSequence mQueryHint; 123e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani private boolean mQueryRefinement; 1240594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani private boolean mClearingFocus; 1255931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani private int mMaxWidth; 1269b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private boolean mVoiceButtonEnabled; 127b47c4fd206ce5d057996d49228342d17ecca027eAmith Yamasani private CharSequence mOldQueryText; 128068d73cf51305a9dfc15c96f17a18676637d3e02Amith Yamasani private CharSequence mUserQuery; 129763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani private boolean mExpandedInActionView; 13053f56c4f51ae11d5b3afde8ce221b3fe9aea3cf1Adam Powell private int mCollapsedImeOptions; 131733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 132733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private SearchableInfo mSearchable; 133940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani private Bundle mAppSearchData; 134733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 135ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell /* 136ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell * SearchView can be set expanded before the IME is ready to be shown during 137ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell * initial UI setup. The show operation is asynchronous to account for this. 138ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell */ 139ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell private Runnable mShowImeRunnable = new Runnable() { 140ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell public void run() { 141ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell InputMethodManager imm = (InputMethodManager) 142ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 143ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell 144ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell if (imm != null) { 145ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell imm.showSoftInputUnchecked(0, null); 146ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell } 147ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell } 148ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell }; 149ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell 150a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani private Runnable mUpdateDrawableStateRunnable = new Runnable() { 151a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani public void run() { 152a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani updateFocusedState(); 153a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani } 154a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani }; 155a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani 1568790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani private Runnable mReleaseCursorRunnable = new Runnable() { 1578790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani public void run() { 1588790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) { 1598790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani mSuggestionsAdapter.changeCursor(null); 1608790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani } 1618790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani } 1628790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani }; 1638790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani 164ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // For voice searching 165ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private final Intent mVoiceWebSearchIntent; 166ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private final Intent mVoiceAppSearchIntent; 167ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 168733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // A weak map of drawables we've gotten from other packages, so we don't load them 169733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // more than once. 170733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = 171733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani new WeakHashMap<String, Drawable.ConstantState>(); 172733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 173733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 174733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Callbacks for changes to the query text. 175733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 17601f21354654a315ea2a2a1668bbda50645123d4aAdam Powell public interface OnQueryTextListener { 177733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 178733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 179733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Called when the user submits the query. This could be due to a key press on the 180733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * keyboard or due to pressing a submit button. 181733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * The listener can override the standard behavior by returning true 182733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * to indicate that it has handled the submit request. Otherwise return false to 183733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * let the SearchView handle the submission by launching any associated intent. 184733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 185733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param query the query text that is to be submitted 186733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 187733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return true if the query has been handled by the listener, false to let the 188733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * SearchView perform the default action. 189733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 19001f21354654a315ea2a2a1668bbda50645123d4aAdam Powell boolean onQueryTextSubmit(String query); 191733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 192733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 193733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Called when the query text is changed by the user. 194733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 195733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param newText the new content of the query text field. 196733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 197733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return false if the SearchView should perform the default action of showing any 198733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * suggestions if available, true if the action was handled by the listener. 199733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 20001f21354654a315ea2a2a1668bbda50645123d4aAdam Powell boolean onQueryTextChange(String newText); 201733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 202733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 203733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public interface OnCloseListener { 204733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 205733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 206733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * The user is attempting to close the SearchView. 207733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 208733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return true if the listener wants to override the default behavior of clearing the 209733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * text field and dismissing it, false otherwise. 210733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 211733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani boolean onClose(); 212733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 213733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 2140594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** 2150594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Callback interface for selection events on suggestions. These callbacks 2160594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}. 2170594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani */ 21801f21354654a315ea2a2a1668bbda50645123d4aAdam Powell public interface OnSuggestionListener { 2190594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 2200594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** 2210594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Called when a suggestion was selected by navigating to it. 2220594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @param position the absolute position in the list of suggestions. 2230594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * 2240594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @return true if the listener handles the event and wants to override the default 2250594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * behavior of possibly rewriting the query based on the selected item, false otherwise. 2260594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani */ 22701f21354654a315ea2a2a1668bbda50645123d4aAdam Powell boolean onSuggestionSelect(int position); 2280594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 2290594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** 2300594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Called when a suggestion was clicked. 2310594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @param position the absolute position of the clicked item in the list of suggestions. 2320594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * 2330594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @return true if the listener handles the event and wants to override the default 2340594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * behavior of launching any intent or submitting a search query specified on that item. 2350594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Return false otherwise. 2360594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani */ 23701f21354654a315ea2a2a1668bbda50645123d4aAdam Powell boolean onSuggestionClick(int position); 2380594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 2390594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 240733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public SearchView(Context context) { 241733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani this(context, null); 242733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 243733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 244733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public SearchView(Context context, AttributeSet attrs) { 245733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani super(context, attrs); 246733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 247733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani LayoutInflater inflater = (LayoutInflater) context 248733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 249733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani inflater.inflate(R.layout.search_view, this, true); 250733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 251733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSearchButton = findViewById(R.id.search_button); 252968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text); 253968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.setSearchView(this); 254968ec938399033d280b1648123104ac567f2a093Amith Yamasani 255733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSearchEditFrame = findViewById(R.id.search_edit_frame); 25679f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani mSearchPlate = findViewById(R.id.search_plate); 2579b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani mSubmitArea = findViewById(R.id.submit_area); 258733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSubmitButton = findViewById(R.id.search_go_btn); 2594aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani mCloseButton = (ImageView) findViewById(R.id.search_close_btn); 260ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceButton = findViewById(R.id.search_voice_btn); 261b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon); 262733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 263733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSearchButton.setOnClickListener(mOnClickListener); 264733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mCloseButton.setOnClickListener(mOnClickListener); 265733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSubmitButton.setOnClickListener(mOnClickListener); 266ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceButton.setOnClickListener(mOnClickListener); 267f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani mQueryTextView.setOnClickListener(mOnClickListener); 268ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 269733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.addTextChangedListener(mTextWatcher); 270733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setOnEditorActionListener(mOnEditorActionListener); 271733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setOnItemClickListener(mOnItemClickListener); 272733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener); 273968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.setOnKeyListener(mTextKeyListener); 274535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin // Inform any listener of focus changes 2750594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() { 2760594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 2770594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani public void onFocusChange(View v, boolean hasFocus) { 2780594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani if (mOnQueryTextFocusChangeListener != null) { 2790594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus); 2800594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 2810594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 2820594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani }); 283733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 284733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0); 285733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true)); 2865931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani int maxWidth = a.getDimensionPixelSize(R.styleable.SearchView_maxWidth, -1); 2875931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani if (maxWidth != -1) { 2885931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani setMaxWidth(maxWidth); 2895931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani } 290c0171d5e8ed1aaeaa658aa0d603860f7ada6807aAdam Powell CharSequence queryHint = a.getText(R.styleable.SearchView_queryHint); 291c0171d5e8ed1aaeaa658aa0d603860f7ada6807aAdam Powell if (!TextUtils.isEmpty(queryHint)) { 292c0171d5e8ed1aaeaa658aa0d603860f7ada6807aAdam Powell setQueryHint(queryHint); 293c0171d5e8ed1aaeaa658aa0d603860f7ada6807aAdam Powell } 2945607a3827172ff40196380d846128e892bedc118Amith Yamasani int imeOptions = a.getInt(R.styleable.SearchView_imeOptions, -1); 2955607a3827172ff40196380d846128e892bedc118Amith Yamasani if (imeOptions != -1) { 2965607a3827172ff40196380d846128e892bedc118Amith Yamasani setImeOptions(imeOptions); 2975607a3827172ff40196380d846128e892bedc118Amith Yamasani } 2985607a3827172ff40196380d846128e892bedc118Amith Yamasani int inputType = a.getInt(R.styleable.SearchView_inputType, -1); 2995607a3827172ff40196380d846128e892bedc118Amith Yamasani if (inputType != -1) { 3005607a3827172ff40196380d846128e892bedc118Amith Yamasani setInputType(inputType); 3015607a3827172ff40196380d846128e892bedc118Amith Yamasani } 3025607a3827172ff40196380d846128e892bedc118Amith Yamasani 303733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani a.recycle(); 304733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 3055607a3827172ff40196380d846128e892bedc118Amith Yamasani boolean focusable = true; 3065607a3827172ff40196380d846128e892bedc118Amith Yamasani 3077f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0); 3087f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani focusable = a.getBoolean(R.styleable.View_focusable, focusable); 3097f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani a.recycle(); 3107f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani setFocusable(focusable); 3117f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani 312ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Save voice intent for later queries/launching 313ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 314ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 315ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 316ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 317ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 318ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 319ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 320ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 321b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor()); 322b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (mDropDownAnchor != null) { 323b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() { 324b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani @Override 325b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani public void onLayoutChange(View v, int left, int top, int right, int bottom, 326b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani int oldLeft, int oldTop, int oldRight, int oldBottom) { 327b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani adjustDropDownSizeAndPosition(); 328b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 329b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani }); 330b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 331b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 332733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani updateViewsVisibility(mIconifiedByDefault); 333b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani updateQueryHint(); 334733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 335733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 336733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 337733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used 338733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * to display labels, hints, suggestions, create intents for launching search results screens 339733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * and controlling other affordances such as a voice button. 340733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 341733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific 342733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * activity or a global search provider. 343733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 344733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setSearchableInfo(SearchableInfo searchable) { 345733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSearchable = searchable; 346733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mSearchable != null) { 347733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani updateSearchAutoComplete(); 34879f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani updateQueryHint(); 349733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 3509b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani // Cache the voice search capability 3519b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani mVoiceButtonEnabled = hasVoiceSearch(); 352535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin 353535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin if (mVoiceButtonEnabled) { 354535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin // Disable the microphone on the keyboard, as a mic is displayed near the text box 355535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin // TODO: use imeOptions to disable voice input when the new API will be available 356535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE); 357535698c57dbbd0d0eef79223293526748a07dfecLuca Zanolin } 358b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani updateViewsVisibility(isIconified()); 359733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 360733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 361940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani /** 362940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani * Sets the APP_DATA for legacy SearchDialog use. 363940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani * @param appSearchData bundle provided by the app when launching the search dialog 364940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani * @hide 365940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani */ 366940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani public void setAppSearchData(Bundle appSearchData) { 367940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani mAppSearchData = appSearchData; 368940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani } 369940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani 3705607a3827172ff40196380d846128e892bedc118Amith Yamasani /** 3715607a3827172ff40196380d846128e892bedc118Amith Yamasani * Sets the IME options on the query text field. 3725607a3827172ff40196380d846128e892bedc118Amith Yamasani * 3735607a3827172ff40196380d846128e892bedc118Amith Yamasani * @see TextView#setImeOptions(int) 3745607a3827172ff40196380d846128e892bedc118Amith Yamasani * @param imeOptions the options to set on the query text field 3755607a3827172ff40196380d846128e892bedc118Amith Yamasani * 3765607a3827172ff40196380d846128e892bedc118Amith Yamasani * @attr ref android.R.styleable#SearchView_imeOptions 3775607a3827172ff40196380d846128e892bedc118Amith Yamasani */ 3785607a3827172ff40196380d846128e892bedc118Amith Yamasani public void setImeOptions(int imeOptions) { 3795607a3827172ff40196380d846128e892bedc118Amith Yamasani mQueryTextView.setImeOptions(imeOptions); 3805607a3827172ff40196380d846128e892bedc118Amith Yamasani } 3815607a3827172ff40196380d846128e892bedc118Amith Yamasani 3825607a3827172ff40196380d846128e892bedc118Amith Yamasani /** 383eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * Returns the IME options set on the query text field. 384eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @return the ime options 385eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @see TextView#setImeOptions(int) 386eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * 387eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @attr ref android.R.styleable#SearchView_imeOptions 388eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani */ 389eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani public int getImeOptions() { 390eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return mQueryTextView.getImeOptions(); 391eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 392eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani 393eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani /** 3945607a3827172ff40196380d846128e892bedc118Amith Yamasani * Sets the input type on the query text field. 3955607a3827172ff40196380d846128e892bedc118Amith Yamasani * 3965607a3827172ff40196380d846128e892bedc118Amith Yamasani * @see TextView#setInputType(int) 3975607a3827172ff40196380d846128e892bedc118Amith Yamasani * @param inputType the input type to set on the query text field 3985607a3827172ff40196380d846128e892bedc118Amith Yamasani * 3995607a3827172ff40196380d846128e892bedc118Amith Yamasani * @attr ref android.R.styleable#SearchView_inputType 4005607a3827172ff40196380d846128e892bedc118Amith Yamasani */ 4015607a3827172ff40196380d846128e892bedc118Amith Yamasani public void setInputType(int inputType) { 4025607a3827172ff40196380d846128e892bedc118Amith Yamasani mQueryTextView.setInputType(inputType); 4035607a3827172ff40196380d846128e892bedc118Amith Yamasani } 4045607a3827172ff40196380d846128e892bedc118Amith Yamasani 405eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani /** 406eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * Returns the input type set on the query text field. 407eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @return the input type 408eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * 409eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @attr ref android.R.styleable#SearchView_inputType 410eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani */ 411eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani public int getInputType() { 412eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return mQueryTextView.getInputType(); 413eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 414eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani 4150594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** @hide */ 4160594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani @Override 4170594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 4187f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani // Don't accept focus if in the middle of clearing focus 4197f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani if (mClearingFocus) return false; 4207f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani // Check if SearchView is focusable. 4217f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani if (!isFocusable()) return false; 4227f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani // If it is not iconified, then give the focus to the text field 4237f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani if (!isIconified()) { 4247f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect); 425f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani if (result) { 426f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani updateViewsVisibility(false); 427f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani } 4287f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani return result; 4297f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani } else { 4307f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani return super.requestFocus(direction, previouslyFocusedRect); 4317f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani } 4320594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 4330594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 4340594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** @hide */ 4350594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani @Override 4360594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani public void clearFocus() { 4370594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mClearingFocus = true; 43810da590839e264735de8a4582021aca0dab81037Amith Yamasani setImeVisibility(false); 4390594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani super.clearFocus(); 4400594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mQueryTextView.clearFocus(); 4410594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mClearingFocus = false; 4420594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 4430594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 444733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 445733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets a listener for user actions within the SearchView. 446733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 447733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param listener the listener object that receives callbacks when the user performs 448733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * actions in the SearchView such as clicking on buttons or typing a query. 449733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 45001f21354654a315ea2a2a1668bbda50645123d4aAdam Powell public void setOnQueryTextListener(OnQueryTextListener listener) { 451733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mOnQueryChangeListener = listener; 452733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 453733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 454733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 4559322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * Sets a listener to inform when the user closes the SearchView. 4569322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * 4579322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * @param listener the listener to call when the user closes the SearchView. 4589322775014432ed6c87c864e98fe482f879ff233Amith Yamasani */ 4599322775014432ed6c87c864e98fe482f879ff233Amith Yamasani public void setOnCloseListener(OnCloseListener listener) { 4609322775014432ed6c87c864e98fe482f879ff233Amith Yamasani mOnCloseListener = listener; 4619322775014432ed6c87c864e98fe482f879ff233Amith Yamasani } 4629322775014432ed6c87c864e98fe482f879ff233Amith Yamasani 4639322775014432ed6c87c864e98fe482f879ff233Amith Yamasani /** 4640594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Sets a listener to inform when the focus of the query text field changes. 4650594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * 4660594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @param listener the listener to inform of focus changes. 4670594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani */ 4680594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) { 4690594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mOnQueryTextFocusChangeListener = listener; 4700594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 4710594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 4720594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** 4730594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * Sets a listener to inform when a suggestion is focused or clicked. 4740594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * 4750594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani * @param listener the listener to inform of suggestion selection events. 4760594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani */ 47701f21354654a315ea2a2a1668bbda50645123d4aAdam Powell public void setOnSuggestionListener(OnSuggestionListener listener) { 4780594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani mOnSuggestionListener = listener; 4790594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 4800594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 4810594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani /** 482483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * Sets a listener to inform when the search button is pressed. This is only 483cccdbe9acb5cd721988b6b01a419c568fe5300b6Scott Main * relevant when the text field is not visible by default. Calling {@link #setIconified 484cccdbe9acb5cd721988b6b01a419c568fe5300b6Scott Main * setIconified(false)} can also cause this listener to be informed. 485483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * 486483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * @param listener the listener to inform when the search button is clicked or 487483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * the text field is programmatically de-iconified. 488483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani */ 489483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani public void setOnSearchClickListener(OnClickListener listener) { 490483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani mOnSearchClickListener = listener; 491483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani } 492483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani 493483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani /** 494483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * Returns the query string currently in the text field. 495483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * 496483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani * @return the query string 497483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani */ 498483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani public CharSequence getQuery() { 499483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani return mQueryTextView.getText(); 500483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani } 501483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani 502483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani /** 503733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets a query string in the text field and optionally submits the query as well. 504733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 505733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param query the query string. This replaces any query text already present in the 506733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * text field. 507733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param submit whether to submit the query right now or only update the contents of 508733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * text field. 509733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 510733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setQuery(CharSequence query, boolean submit) { 511733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setText(query); 51287c5025fb8a21348ead7cd7e6fa08aa5dc0f501eDmitri Plotnikov if (query != null) { 5132427192e58b2ff31c3238b6c04440a15f4e7bf20Gilles Debunne mQueryTextView.setSelection(mQueryTextView.length()); 514068d73cf51305a9dfc15c96f17a18676637d3e02Amith Yamasani mUserQuery = query; 51587c5025fb8a21348ead7cd7e6fa08aa5dc0f501eDmitri Plotnikov } 51687c5025fb8a21348ead7cd7e6fa08aa5dc0f501eDmitri Plotnikov 517733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // If the query is not empty and submit is requested, submit the query 518733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (submit && !TextUtils.isEmpty(query)) { 519733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani onSubmitQuery(); 520733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 521733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 522733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 523733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 524733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets the hint text to display in the query text field. This overrides any hint specified 525733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * in the SearchableInfo. 526733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 527733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param hint the hint text to display 528abdf0d533c292709e51cee2c1213d5e44baca963Scott Main * 529abdf0d533c292709e51cee2c1213d5e44baca963Scott Main * @attr ref android.R.styleable#SearchView_queryHint 530733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 531733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setQueryHint(CharSequence hint) { 532733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryHint = hint; 533733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani updateQueryHint(); 534733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 535733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 536733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 537eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * Gets the hint text to display in the query text field. 538eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @return the query hint text, if specified, null otherwise. 539eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * 540eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @attr ref android.R.styleable#SearchView_queryHint 541eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani */ 542eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani public CharSequence getQueryHint() { 543eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani if (mQueryHint != null) { 544eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return mQueryHint; 545eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } else if (mSearchable != null) { 546eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani CharSequence hint = null; 547eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani int hintId = mSearchable.getHintId(); 548eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani if (hintId != 0) { 549eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani hint = getContext().getString(hintId); 550eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 551eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return hint; 552eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 553eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return null; 554eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 555eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani 556eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani /** 557733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets the default or resting state of the search field. If true, a single search icon is 558733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * shown by default and expands to show the text field and other buttons when pressed. Also, 559733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * if the default state is iconified, then it collapses to that state when the close button 5609322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * is pressed. Changes to this property will take effect immediately. 5619322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * 562cccdbe9acb5cd721988b6b01a419c568fe5300b6Scott Main * <p>The default value is true.</p> 563733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 5649322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * @param iconified whether the search field should be iconified by default 565abdf0d533c292709e51cee2c1213d5e44baca963Scott Main * 566abdf0d533c292709e51cee2c1213d5e44baca963Scott Main * @attr ref android.R.styleable#SearchView_iconifiedByDefault 567733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 568733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setIconifiedByDefault(boolean iconified) { 569ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (mIconifiedByDefault == iconified) return; 570733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mIconifiedByDefault = iconified; 571733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani updateViewsVisibility(iconified); 572b47c4fd206ce5d057996d49228342d17ecca027eAmith Yamasani updateQueryHint(); 573733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 574733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 5759322775014432ed6c87c864e98fe482f879ff233Amith Yamasani /** 5769322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * Returns the default iconified state of the search field. 5779322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * @return 5783d3e7a5816b5c78687bbfa4ef32abb8f1a5b83e1Amith Yamasani * 5793d3e7a5816b5c78687bbfa4ef32abb8f1a5b83e1Amith Yamasani * @attr ref android.R.styleable#SearchView_iconifiedByDefault 5809322775014432ed6c87c864e98fe482f879ff233Amith Yamasani */ 581733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public boolean isIconfiedByDefault() { 582733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return mIconifiedByDefault; 583733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 584733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 585733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 5869322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is 5879322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * a temporary state and does not override the default iconified state set by 5889322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then 5899322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * a false here will only be valid until the user closes the field. And if the default 5909322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * state is expanded, then a true here will only clear the text field and not close it. 5919322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * 5929322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * @param iconify a true value will collapse the SearchView to an icon, while a false will 5939322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * expand it. 5949322775014432ed6c87c864e98fe482f879ff233Amith Yamasani */ 5959322775014432ed6c87c864e98fe482f879ff233Amith Yamasani public void setIconified(boolean iconify) { 5969322775014432ed6c87c864e98fe482f879ff233Amith Yamasani if (iconify) { 5979322775014432ed6c87c864e98fe482f879ff233Amith Yamasani onCloseClicked(); 5989322775014432ed6c87c864e98fe482f879ff233Amith Yamasani } else { 5999322775014432ed6c87c864e98fe482f879ff233Amith Yamasani onSearchClicked(); 6009322775014432ed6c87c864e98fe482f879ff233Amith Yamasani } 6019322775014432ed6c87c864e98fe482f879ff233Amith Yamasani } 6029322775014432ed6c87c864e98fe482f879ff233Amith Yamasani 6039322775014432ed6c87c864e98fe482f879ff233Amith Yamasani /** 6049322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * Returns the current iconified state of the SearchView. 6059322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * 6069322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * @return true if the SearchView is currently iconified, false if the search field is 6079322775014432ed6c87c864e98fe482f879ff233Amith Yamasani * fully visible. 6089322775014432ed6c87c864e98fe482f879ff233Amith Yamasani */ 6099322775014432ed6c87c864e98fe482f879ff233Amith Yamasani public boolean isIconified() { 6109322775014432ed6c87c864e98fe482f879ff233Amith Yamasani return mIconified; 6119322775014432ed6c87c864e98fe482f879ff233Amith Yamasani } 6129322775014432ed6c87c864e98fe482f879ff233Amith Yamasani 6139322775014432ed6c87c864e98fe482f879ff233Amith Yamasani /** 614733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Enables showing a submit button when the query is non-empty. In cases where the SearchView 615733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * is being used to filter the contents of the current activity and doesn't launch a separate 616733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * results activity, then the submit button should be disabled. 617733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 618733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param enabled true to show a submit button for submitting queries, false if a submit 619733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * button is not required. 620733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 621733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setSubmitButtonEnabled(boolean enabled) { 622733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSubmitButtonEnabled = enabled; 623ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani updateViewsVisibility(isIconified()); 624733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 625733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 626733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 627733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Returns whether the submit button is enabled when necessary or never displayed. 628733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 629733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return whether the submit button is enabled automatically when necessary 630733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 631733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public boolean isSubmitButtonEnabled() { 632733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return mSubmitButtonEnabled; 633733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 634733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 635e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani /** 636e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * Specifies if a query refinement button should be displayed alongside each suggestion 637e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * or if it should depend on the flags set in the individual items retrieved from the 638e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * suggestions provider. Clicking on the query refinement button will replace the text 639e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * in the query text field with the text from the suggestion. This flag only takes effect 640e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)} 641e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * and not when using a custom adapter. 642e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * 643e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * @param enable true if all items should have a query refinement button, false if only 644e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * those items that have a query refinement flag set should have the button. 645e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * 646e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * @see SearchManager#SUGGEST_COLUMN_FLAGS 647e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * @see SearchManager#FLAG_QUERY_REFINEMENT 648e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani */ 649e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani public void setQueryRefinementEnabled(boolean enable) { 650e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani mQueryRefinement = enable; 651e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani if (mSuggestionsAdapter instanceof SuggestionsAdapter) { 652e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( 653e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY); 654e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani } 655e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani } 656e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani 657e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani /** 658e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * Returns whether query refinement is enabled for all items or only specific ones. 659e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * @return true if enabled for all items, false otherwise. 660e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani */ 661e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani public boolean isQueryRefinementEnabled() { 662e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani return mQueryRefinement; 663733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 664733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 665733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 666733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * You can set a custom adapter if you wish. Otherwise the default adapter is used to 667733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * display the suggestions from the suggestions provider associated with the SearchableInfo. 668733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 669733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @see #setSearchableInfo(SearchableInfo) 670733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 671733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void setSuggestionsAdapter(CursorAdapter adapter) { 672733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSuggestionsAdapter = adapter; 673733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 674733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setAdapter(mSuggestionsAdapter); 675733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 676733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 677733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 678733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Returns the adapter used for suggestions, if any. 679733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return the suggestions adapter 680733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 681733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public CursorAdapter getSuggestionsAdapter() { 682733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return mSuggestionsAdapter; 683733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 684733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 6855931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani /** 6865931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * Makes the view at most this many pixels wide 6875931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * 6885931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani * @attr ref android.R.styleable#SearchView_maxWidth 6895931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani */ 6905931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani public void setMaxWidth(int maxpixels) { 6915931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani mMaxWidth = maxpixels; 6925931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani 6935931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani requestLayout(); 6945931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani } 6955931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani 696eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani /** 697eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * Gets the specified maximum width in pixels, if set. Returns zero if 698eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * no maximum width was specified. 699eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani * @return the maximum width of the view 7003d3e7a5816b5c78687bbfa4ef32abb8f1a5b83e1Amith Yamasani * 7013d3e7a5816b5c78687bbfa4ef32abb8f1a5b83e1Amith Yamasani * @attr ref android.R.styleable#SearchView_maxWidth 702eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani */ 703eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani public int getMaxWidth() { 704eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani return mMaxWidth; 705eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani } 706eca59d39c3e92815002dd50aaf176100a02e5a47Amith Yamasani 7075931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani @Override 7085931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 709a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani // Let the standard measurements take effect in iconified state. 710a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani if (isIconified()) { 711a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani super.onMeasure(widthMeasureSpec, heightMeasureSpec); 712a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani return; 713a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani } 714a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani 7155931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani int widthMode = MeasureSpec.getMode(widthMeasureSpec); 7165931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani int width = MeasureSpec.getSize(widthMeasureSpec); 7175931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani 718167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani switch (widthMode) { 719167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani case MeasureSpec.AT_MOST: 720167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani // If there is an upper limit, don't exceed maximum width (explicit or implicit) 721167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani if (mMaxWidth > 0) { 722167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani width = Math.min(mMaxWidth, width); 723167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani } else { 724167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani width = Math.min(getPreferredWidth(), width); 725167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani } 726167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani break; 727167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani case MeasureSpec.EXACTLY: 728167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani // If an exact width is specified, still don't exceed any specified maximum width 729167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani if (mMaxWidth > 0) { 730167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani width = Math.min(mMaxWidth, width); 731167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani } 732167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani break; 733167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani case MeasureSpec.UNSPECIFIED: 734167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani // Use maximum width, if specified, else preferred width 735167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth(); 736167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani break; 7375931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani } 738167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani widthMode = MeasureSpec.EXACTLY; 739167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec); 740167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani } 741167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani 742167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani private int getPreferredWidth() { 743167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani return getContext().getResources() 744167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani .getDimensionPixelSize(R.dimen.search_view_preferred_width); 7455931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani } 7465931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani 7479322775014432ed6c87c864e98fe482f879ff233Amith Yamasani private void updateViewsVisibility(final boolean collapsed) { 7489322775014432ed6c87c864e98fe482f879ff233Amith Yamasani mIconified = collapsed; 749733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // Visibility of views that are visible when collapsed 7509322775014432ed6c87c864e98fe482f879ff233Amith Yamasani final int visCollapsed = collapsed ? VISIBLE : GONE; 7510594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani // Is there text in the query 7520594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); 753733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 754733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSearchButton.setVisibility(visCollapsed); 7559b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani updateSubmitButton(hasText); 7569b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); 757b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); 7584aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani updateCloseButton(); 759ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani updateVoiceButton(!hasText); 7609b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani updateSubmitArea(); 7619b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7629b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani 7639b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private boolean hasVoiceSearch() { 7649b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) { 7659b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani Intent testIntent = null; 7669b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani if (mSearchable.getVoiceSearchLaunchWebSearch()) { 7679b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani testIntent = mVoiceWebSearchIntent; 7689b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { 7699b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani testIntent = mVoiceAppSearchIntent; 7709b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7719b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani if (testIntent != null) { 7729b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent, 7739b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani PackageManager.MATCH_DEFAULT_ONLY); 7749b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani return ri != null; 7759b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7769b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7779b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani return false; 7789b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7799b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani 7809b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private boolean isSubmitAreaEnabled() { 7819b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified(); 7829b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7839b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani 7849b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private void updateSubmitButton(boolean hasText) { 78579f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani int visibility = GONE; 786cf72ab4ddebae48e34dd2d32b7da8bf5a5d7cfa0Amith Yamasani if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus() 787cf72ab4ddebae48e34dd2d32b7da8bf5a5d7cfa0Amith Yamasani && (hasText || !mVoiceButtonEnabled)) { 78879f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani visibility = VISIBLE; 78979f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani } 79079f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani mSubmitButton.setVisibility(visibility); 7919b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 7929b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani 7939b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani private void updateSubmitArea() { 7949b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani int visibility = GONE; 79579f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani if (isSubmitAreaEnabled() 79679f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani && (mSubmitButton.getVisibility() == VISIBLE 79779f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani || mVoiceButton.getVisibility() == VISIBLE)) { 79879f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani visibility = VISIBLE; 7999b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani } 8009b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani mSubmitArea.setVisibility(visibility); 801733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 802733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 8034aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani private void updateCloseButton() { 8044aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText()); 8054aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani // Should we show the close button? It is not shown if there's no focus, 8064aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani // field is not iconified by default and there is no text in it. 807763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView); 808167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani mCloseButton.setVisibility(showClose ? VISIBLE : GONE); 8094aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET); 8104aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani } 8114aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani 812a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani private void postUpdateFocusedState() { 813a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani post(mUpdateDrawableStateRunnable); 814a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani } 815a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani 816a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani private void updateFocusedState() { 817a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani boolean focused = mQueryTextView.hasFocus(); 81879f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); 81979f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET); 820a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani invalidate(); 821a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani } 822a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani 823a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani @Override 824a465b2d4c34a24e42e5356b3632cc9c071fc9739Amith Yamasani protected void onDetachedFromWindow() { 825a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani removeCallbacks(mUpdateDrawableStateRunnable); 8268790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani post(mReleaseCursorRunnable); 827a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani super.onDetachedFromWindow(); 82879f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani } 82979f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani 830ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell private void setImeVisibility(final boolean visible) { 831ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell if (visible) { 832ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell post(mShowImeRunnable); 833ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell } else { 834ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell removeCallbacks(mShowImeRunnable); 835ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell InputMethodManager imm = (InputMethodManager) 836ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 8370594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani 838ccdd4ee44f8cfbb45b2989cca833895fcc4c4225Adam Powell if (imm != null) { 8390594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani imm.hideSoftInputFromWindow(getWindowToken(), 0); 840733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 841733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 842733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 843733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 844e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani /** 845e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * Called by the SuggestionsAdapter 846e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani * @hide 847e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani */ 848e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani /* package */void onQueryRefine(CharSequence queryText) { 849e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani setQuery(queryText); 850e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani } 851e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani 852733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private final OnClickListener mOnClickListener = new OnClickListener() { 853733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 854733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void onClick(View v) { 855733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (v == mSearchButton) { 856733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani onSearchClicked(); 857733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } else if (v == mCloseButton) { 858733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani onCloseClicked(); 859733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } else if (v == mSubmitButton) { 860733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani onSubmitQuery(); 861ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } else if (v == mVoiceButton) { 862ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani onVoiceClicked(); 863f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani } else if (v == mQueryTextView) { 864f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani forceSuggestionQuery(); 865733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 866733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 867733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani }; 868733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 869733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 870733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Handles the key down event for dealing with action keys. 871733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 872733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param keyCode This is the keycode of the typed key, and is the same value as 873733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * found in the KeyEvent parameter. 874733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param event The complete event record for the typed key 875733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 876733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return true if the event was handled here, or false if not. 877733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 878733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani @Override 879733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public boolean onKeyDown(int keyCode, KeyEvent event) { 880733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mSearchable == null) { 881733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return false; 882733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 883733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 884733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // if it's an action specified by the searchable activity, launch the 885733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // entered query with the action key 886733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 887733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { 8889322775014432ed6c87c864e98fe482f879ff233Amith Yamasani launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText() 8899322775014432ed6c87c864e98fe482f879ff233Amith Yamasani .toString()); 890733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return true; 891733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 892733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 893733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return super.onKeyDown(keyCode, event); 894733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 895733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 896968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 897968ec938399033d280b1648123104ac567f2a093Amith Yamasani * React to the user typing "enter" or other hardwired keys while typing in 898968ec938399033d280b1648123104ac567f2a093Amith Yamasani * the search box. This handles these special keys while the edit box has 899968ec938399033d280b1648123104ac567f2a093Amith Yamasani * focus. 900968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 901968ec938399033d280b1648123104ac567f2a093Amith Yamasani View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { 902968ec938399033d280b1648123104ac567f2a093Amith Yamasani public boolean onKey(View v, int keyCode, KeyEvent event) { 903968ec938399033d280b1648123104ac567f2a093Amith Yamasani // guard against possible race conditions 904968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mSearchable == null) { 905968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 906968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 907968ec938399033d280b1648123104ac567f2a093Amith Yamasani 908968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (DBG) { 909968ec938399033d280b1648123104ac567f2a093Amith Yamasani Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: " 910968ec938399033d280b1648123104ac567f2a093Amith Yamasani + mQueryTextView.getListSelection()); 911968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 912968ec938399033d280b1648123104ac567f2a093Amith Yamasani 913968ec938399033d280b1648123104ac567f2a093Amith Yamasani // If a suggestion is selected, handle enter, search key, and action keys 914968ec938399033d280b1648123104ac567f2a093Amith Yamasani // as presses on the selected suggestion 915968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mQueryTextView.isPopupShowing() 916968ec938399033d280b1648123104ac567f2a093Amith Yamasani && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) { 917968ec938399033d280b1648123104ac567f2a093Amith Yamasani return onSuggestionsKey(v, keyCode, event); 918968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 919968ec938399033d280b1648123104ac567f2a093Amith Yamasani 920968ec938399033d280b1648123104ac567f2a093Amith Yamasani // If there is text in the query box, handle enter, and action keys 921968ec938399033d280b1648123104ac567f2a093Amith Yamasani // The search key is handled by the dialog's onKeyDown(). 9224e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown if (!mQueryTextView.isEmpty() && event.hasNoModifiers()) { 9234e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown if (event.getAction() == KeyEvent.ACTION_UP) { 9244e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown if (keyCode == KeyEvent.KEYCODE_ENTER) { 9254e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown v.cancelLongPress(); 9264e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown 9274e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown // Launch as a regular search. 9284e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText() 9294e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown .toString()); 9304e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown return true; 9314e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown } 932968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 933968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (event.getAction() == KeyEvent.ACTION_DOWN) { 934968ec938399033d280b1648123104ac567f2a093Amith Yamasani SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 935968ec938399033d280b1648123104ac567f2a093Amith Yamasani if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { 936968ec938399033d280b1648123104ac567f2a093Amith Yamasani launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView 937968ec938399033d280b1648123104ac567f2a093Amith Yamasani .getText().toString()); 938968ec938399033d280b1648123104ac567f2a093Amith Yamasani return true; 939968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 940968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 941968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 942968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 943968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 944968ec938399033d280b1648123104ac567f2a093Amith Yamasani }; 945968ec938399033d280b1648123104ac567f2a093Amith Yamasani 946968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 947968ec938399033d280b1648123104ac567f2a093Amith Yamasani * React to the user typing while in the suggestions list. First, check for 948968ec938399033d280b1648123104ac567f2a093Amith Yamasani * action keys. If not handled, try refocusing regular characters into the 949968ec938399033d280b1648123104ac567f2a093Amith Yamasani * EditText. 950968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 951968ec938399033d280b1648123104ac567f2a093Amith Yamasani private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { 952968ec938399033d280b1648123104ac567f2a093Amith Yamasani // guard against possible race conditions (late arrival after dismiss) 953968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mSearchable == null) { 954968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 955968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 956968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mSuggestionsAdapter == null) { 957968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 958968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 9594e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown if (event.getAction() == KeyEvent.ACTION_DOWN && event.hasNoModifiers()) { 960968ec938399033d280b1648123104ac567f2a093Amith Yamasani // First, check for enter or search (both of which we'll treat as a 961968ec938399033d280b1648123104ac567f2a093Amith Yamasani // "click") 9624e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH 9634e6319b73c85082e18d1c532b86336ddd1f8cfaaJeff Brown || keyCode == KeyEvent.KEYCODE_TAB) { 964968ec938399033d280b1648123104ac567f2a093Amith Yamasani int position = mQueryTextView.getListSelection(); 965968ec938399033d280b1648123104ac567f2a093Amith Yamasani return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); 966968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 967968ec938399033d280b1648123104ac567f2a093Amith Yamasani 968968ec938399033d280b1648123104ac567f2a093Amith Yamasani // Next, check for left/right moves, which we use to "return" the 969968ec938399033d280b1648123104ac567f2a093Amith Yamasani // user to the edit view 970968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 971968ec938399033d280b1648123104ac567f2a093Amith Yamasani // give "focus" to text editor, with cursor at the beginning if 972968ec938399033d280b1648123104ac567f2a093Amith Yamasani // left key, at end if right key 973968ec938399033d280b1648123104ac567f2a093Amith Yamasani // TODO: Reverse left/right for right-to-left languages, e.g. 974968ec938399033d280b1648123104ac567f2a093Amith Yamasani // Arabic 975968ec938399033d280b1648123104ac567f2a093Amith Yamasani int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView 976968ec938399033d280b1648123104ac567f2a093Amith Yamasani .length(); 977968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.setSelection(selPoint); 978968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.setListSelection(0); 979968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.clearListSelection(); 980968ec938399033d280b1648123104ac567f2a093Amith Yamasani mQueryTextView.ensureImeVisible(true); 981968ec938399033d280b1648123104ac567f2a093Amith Yamasani 982968ec938399033d280b1648123104ac567f2a093Amith Yamasani return true; 983968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 984968ec938399033d280b1648123104ac567f2a093Amith Yamasani 985968ec938399033d280b1648123104ac567f2a093Amith Yamasani // Next, check for an "up and out" move 986968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) { 987968ec938399033d280b1648123104ac567f2a093Amith Yamasani // TODO: restoreUserQuery(); 988968ec938399033d280b1648123104ac567f2a093Amith Yamasani // let ACTV complete the move 989968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 990968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 991968ec938399033d280b1648123104ac567f2a093Amith Yamasani 992968ec938399033d280b1648123104ac567f2a093Amith Yamasani // Next, check for an "action key" 993968ec938399033d280b1648123104ac567f2a093Amith Yamasani SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 994968ec938399033d280b1648123104ac567f2a093Amith Yamasani if ((actionKey != null) 995968ec938399033d280b1648123104ac567f2a093Amith Yamasani && ((actionKey.getSuggestActionMsg() != null) || (actionKey 996968ec938399033d280b1648123104ac567f2a093Amith Yamasani .getSuggestActionMsgColumn() != null))) { 997968ec938399033d280b1648123104ac567f2a093Amith Yamasani // launch suggestion using action key column 998968ec938399033d280b1648123104ac567f2a093Amith Yamasani int position = mQueryTextView.getListSelection(); 999968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (position != ListView.INVALID_POSITION) { 1000968ec938399033d280b1648123104ac567f2a093Amith Yamasani Cursor c = mSuggestionsAdapter.getCursor(); 1001968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (c.moveToPosition(position)) { 1002968ec938399033d280b1648123104ac567f2a093Amith Yamasani final String actionMsg = getActionKeyMessage(c, actionKey); 1003968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (actionMsg != null && (actionMsg.length() > 0)) { 1004968ec938399033d280b1648123104ac567f2a093Amith Yamasani return onItemClicked(position, keyCode, actionMsg); 1005968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1006968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1007968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1008968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1009968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1010968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 1011968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1012968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1013968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1014968ec938399033d280b1648123104ac567f2a093Amith Yamasani * For a given suggestion and a given cursor row, get the action message. If 1015968ec938399033d280b1648123104ac567f2a093Amith Yamasani * not provided by the specific row/column, also check for a single 1016968ec938399033d280b1648123104ac567f2a093Amith Yamasani * definition (for the action key). 1017968ec938399033d280b1648123104ac567f2a093Amith Yamasani * 1018968ec938399033d280b1648123104ac567f2a093Amith Yamasani * @param c The cursor providing suggestions 1019968ec938399033d280b1648123104ac567f2a093Amith Yamasani * @param actionKey The actionkey record being examined 1020968ec938399033d280b1648123104ac567f2a093Amith Yamasani * 1021968ec938399033d280b1648123104ac567f2a093Amith Yamasani * @return Returns a string, or null if no action key message for this 1022968ec938399033d280b1648123104ac567f2a093Amith Yamasani * suggestion 1023968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1024968ec938399033d280b1648123104ac567f2a093Amith Yamasani private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) { 1025968ec938399033d280b1648123104ac567f2a093Amith Yamasani String result = null; 1026968ec938399033d280b1648123104ac567f2a093Amith Yamasani // check first in the cursor data, for a suggestion-specific message 1027968ec938399033d280b1648123104ac567f2a093Amith Yamasani final String column = actionKey.getSuggestActionMsgColumn(); 1028968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (column != null) { 1029968ec938399033d280b1648123104ac567f2a093Amith Yamasani result = SuggestionsAdapter.getColumnString(c, column); 1030968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1031968ec938399033d280b1648123104ac567f2a093Amith Yamasani // If the cursor didn't give us a message, see if there's a single 1032968ec938399033d280b1648123104ac567f2a093Amith Yamasani // message defined 1033968ec938399033d280b1648123104ac567f2a093Amith Yamasani // for the actionkey (for all suggestions) 1034968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (result == null) { 1035968ec938399033d280b1648123104ac567f2a093Amith Yamasani result = actionKey.getSuggestActionMsg(); 1036968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1037968ec938399033d280b1648123104ac567f2a093Amith Yamasani return result; 1038968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1039968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1040b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani private int getSearchIconId() { 1041b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani TypedValue outValue = new TypedValue(); 1042b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani getContext().getTheme().resolveAttribute(com.android.internal.R.attr.searchViewSearchIcon, 1043b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani outValue, true); 1044b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani return outValue.resourceId; 1045b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1046b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1047b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani private CharSequence getDecoratedHint(CharSequence hintText) { 1048b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // If the field is always expanded, then don't add the search icon to the hint 1049b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (!mIconifiedByDefault) return hintText; 1050b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1051b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon 1052b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani ssb.append(hintText); 1053b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId()); 1054b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani int textSize = (int) (mQueryTextView.getTextSize() * 1.25); 1055b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani searchIcon.setBounds(0, 0, textSize, textSize); 1056b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 1057b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani return ssb; 1058b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1059b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1060733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void updateQueryHint() { 1061733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mQueryHint != null) { 1062b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mQueryTextView.setHint(getDecoratedHint(mQueryHint)); 1063733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } else if (mSearchable != null) { 1064733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani CharSequence hint = null; 1065733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani int hintId = mSearchable.getHintId(); 1066733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (hintId != 0) { 1067733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani hint = getContext().getString(hintId); 1068733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1069733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (hint != null) { 1070b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mQueryTextView.setHint(getDecoratedHint(hint)); 1071733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1072b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } else { 1073b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mQueryTextView.setHint(getDecoratedHint("")); 1074733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1075733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1076733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1077733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1078733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Updates the auto-complete text view. 1079733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1080733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void updateSearchAutoComplete() { 1081733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setDropDownAnimationStyle(0); // no animation 10825931b1f415fdb30f429fb39238c63f7533335998Amith Yamasani mQueryTextView.setThreshold(mSearchable.getSuggestThreshold()); 10835607a3827172ff40196380d846128e892bedc118Amith Yamasani mQueryTextView.setImeOptions(mSearchable.getImeOptions()); 10845607a3827172ff40196380d846128e892bedc118Amith Yamasani int inputType = mSearchable.getInputType(); 10855607a3827172ff40196380d846128e892bedc118Amith Yamasani // We only touch this if the input type is set up for text (which it almost certainly 10865607a3827172ff40196380d846128e892bedc118Amith Yamasani // should be, in the case of search!) 10875607a3827172ff40196380d846128e892bedc118Amith Yamasani if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) { 10885607a3827172ff40196380d846128e892bedc118Amith Yamasani // The existence of a suggestions authority is the proxy for "suggestions 10895607a3827172ff40196380d846128e892bedc118Amith Yamasani // are available here" 10905607a3827172ff40196380d846128e892bedc118Amith Yamasani inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 10915607a3827172ff40196380d846128e892bedc118Amith Yamasani if (mSearchable.getSuggestAuthority() != null) { 10925607a3827172ff40196380d846128e892bedc118Amith Yamasani inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 10939ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing 10949ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // auto-completion based on its own semantics, which it will present to the user 10959ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // as they type. This generally means that the input method should not show its 10969ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // own candidates, and the spell checker should not be in action. The text editor 10979ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // supplies its candidates by calling InputMethodManager.displayCompletions(), 10989ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka // which in turn will call InputMethodSession.displayCompletions(). 10999ce1116ee844c7b03ca9c09956992b9a85b7a247Satoshi Kataoka inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; 11005607a3827172ff40196380d846128e892bedc118Amith Yamasani } 11015607a3827172ff40196380d846128e892bedc118Amith Yamasani } 11025607a3827172ff40196380d846128e892bedc118Amith Yamasani mQueryTextView.setInputType(inputType); 11038790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani if (mSuggestionsAdapter != null) { 11048790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani mSuggestionsAdapter.changeCursor(null); 11058790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani } 1106733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // attach the suggestions adapter, if suggestions are available 1107733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // The existence of a suggestions authority is the proxy for "suggestions available here" 1108733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mSearchable.getSuggestAuthority() != null) { 1109733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mSuggestionsAdapter = new SuggestionsAdapter(getContext(), 1110733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani this, mSearchable, mOutsideDrawablesCache); 1111733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani mQueryTextView.setAdapter(mSuggestionsAdapter); 1112e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement( 1113e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani mQueryRefinement ? SuggestionsAdapter.REFINE_ALL 1114e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani : SuggestionsAdapter.REFINE_BY_ENTRY); 1115733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1116733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1117733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1118ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani /** 1119ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * Update the visibility of the voice button. There are actually two voice search modes, 1120ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * either of which will activate the button. 1121ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * @param empty whether the search query text field is empty. If it is, then the other 112279f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani * criteria apply to make the voice button visible. 1123ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani */ 1124ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private void updateVoiceButton(boolean empty) { 112579f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani int visibility = GONE; 1126167d69ac41d8a1446ab216e3821ecbcccd8291bbAmith Yamasani if (mVoiceButtonEnabled && !isIconified() && empty) { 11279b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani visibility = VISIBLE; 11289b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani mSubmitButton.setVisibility(GONE); 1129ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1130ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mVoiceButton.setVisibility(visibility); 1131ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1132ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1133733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() { 1134733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1135733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1136733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Called when the input method default action key is pressed. 1137733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1138733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 1139733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani onSubmitQuery(); 1140733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return true; 1141733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1142733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani }; 1143733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1144733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void onTextChanged(CharSequence newText) { 1145733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani CharSequence text = mQueryTextView.getText(); 1146068d73cf51305a9dfc15c96f17a18676637d3e02Amith Yamasani mUserQuery = text; 1147733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani boolean hasText = !TextUtils.isEmpty(text); 1148cf72ab4ddebae48e34dd2d32b7da8bf5a5d7cfa0Amith Yamasani updateSubmitButton(hasText); 1149ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani updateVoiceButton(!hasText); 115073e00df15e07933af06eac50901e50e07f9865bbAmith Yamasani updateCloseButton(); 11519b2e302ab8dff554fdb4a7f9d005496a655f8f03Amith Yamasani updateSubmitArea(); 1152b47c4fd206ce5d057996d49228342d17ecca027eAmith Yamasani if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) { 115301f21354654a315ea2a2a1668bbda50645123d4aAdam Powell mOnQueryChangeListener.onQueryTextChange(newText.toString()); 1154ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1155b47c4fd206ce5d057996d49228342d17ecca027eAmith Yamasani mOldQueryText = newText.toString(); 1156733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1157733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1158733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void onSubmitQuery() { 1159733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani CharSequence query = mQueryTextView.getText(); 11606a7421ba94b1fca0a20ddf146054ebd771ee471eAmith Yamasani if (query != null && TextUtils.getTrimmedLength(query) > 0) { 1161733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mOnQueryChangeListener == null 116201f21354654a315ea2a2a1668bbda50645123d4aAdam Powell || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) { 1163733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (mSearchable != null) { 1164733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString()); 11650594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani setImeVisibility(false); 1166733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1167ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani dismissSuggestions(); 1168733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1169733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1170733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1171733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1172ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private void dismissSuggestions() { 1173ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani mQueryTextView.dismissDropDown(); 1174ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1175ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1176733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void onCloseClicked() { 1177246529891ee289e8393ad4a486db785ef455c778Amith Yamasani CharSequence text = mQueryTextView.getText(); 1178246529891ee289e8393ad4a486db785ef455c778Amith Yamasani if (TextUtils.isEmpty(text)) { 1179246529891ee289e8393ad4a486db785ef455c778Amith Yamasani if (mIconifiedByDefault) { 1180b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // If the app doesn't override the close behavior 1181b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (mOnCloseListener == null || !mOnCloseListener.onClose()) { 1182b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // hide the keyboard and remove focus 1183b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani clearFocus(); 1184b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // collapse the search field 1185b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani updateViewsVisibility(true); 1186b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 11870594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani } 1188246529891ee289e8393ad4a486db785ef455c778Amith Yamasani } else { 1189246529891ee289e8393ad4a486db785ef455c778Amith Yamasani mQueryTextView.setText(""); 1190246529891ee289e8393ad4a486db785ef455c778Amith Yamasani mQueryTextView.requestFocus(); 1191246529891ee289e8393ad4a486db785ef455c778Amith Yamasani setImeVisibility(true); 1192246529891ee289e8393ad4a486db785ef455c778Amith Yamasani } 1193246529891ee289e8393ad4a486db785ef455c778Amith Yamasani 1194733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1195733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1196733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void onSearchClicked() { 1197733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani updateViewsVisibility(false); 11987f8aef6d5bd26cfc7f96d059ab66abdb320ebd27Amith Yamasani mQueryTextView.requestFocus(); 11990594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani setImeVisibility(true); 1200483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani if (mOnSearchClickListener != null) { 1201483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani mOnSearchClickListener.onClick(this); 1202483854820945ae8b4e4512b696cce7da92deed20Amith Yamasani } 1203733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1204733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1205ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private void onVoiceClicked() { 1206ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // guard against possible race conditions 1207ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (mSearchable == null) { 1208ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani return; 1209ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1210ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani SearchableInfo searchable = mSearchable; 1211ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani try { 1212ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (searchable.getVoiceSearchLaunchWebSearch()) { 1213ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent, 1214ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani searchable); 1215ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani getContext().startActivity(webSearchIntent); 1216ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } else if (searchable.getVoiceSearchLaunchRecognizer()) { 1217ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent, 1218ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani searchable); 1219ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani getContext().startActivity(appSearchIntent); 1220ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1221ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } catch (ActivityNotFoundException e) { 1222ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Should not happen, since we check the availability of 1223ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // voice search before showing the button. But just in case... 1224ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Log.w(LOG_TAG, "Could not find voice search activity"); 1225ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1226ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1227ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 12284aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani void onTextFocusChanged() { 122979f7430bf93a9b5dc2b4e0f418f320affdfbf77bAmith Yamasani updateViewsVisibility(isIconified()); 1230a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani // Delayed update to make sure that the focus has settled down and window focus changes 1231a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani // don't affect it. A synchronous update was not working. 1232a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani postUpdateFocusedState(); 1233f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani if (mQueryTextView.hasFocus()) { 1234f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani forceSuggestionQuery(); 1235f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani } 12364aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani } 12374aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani 1238b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani @Override 1239a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani public void onWindowFocusChanged(boolean hasWindowFocus) { 1240a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani super.onWindowFocusChanged(hasWindowFocus); 1241a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani 1242a95e488bdcef3737287227419cfe214bef0556aaAmith Yamasani postUpdateFocusedState(); 1243b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1244b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1245763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani /** 1246763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * {@inheritDoc} 1247763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani */ 1248763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani @Override 1249763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani public void onActionViewCollapsed() { 125010da590839e264735de8a4582021aca0dab81037Amith Yamasani clearFocus(); 125110da590839e264735de8a4582021aca0dab81037Amith Yamasani updateViewsVisibility(true); 125253f56c4f51ae11d5b3afde8ce221b3fe9aea3cf1Adam Powell mQueryTextView.setImeOptions(mCollapsedImeOptions); 1253763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani mExpandedInActionView = false; 1254763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani } 1255763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani 1256763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani /** 1257763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani * {@inheritDoc} 1258763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani */ 1259763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani @Override 1260763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani public void onActionViewExpanded() { 1261434c73ffd85369e220261bda3ac5dbe911304a4cAmith Yamasani if (mExpandedInActionView) return; 1262434c73ffd85369e220261bda3ac5dbe911304a4cAmith Yamasani 1263763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani mExpandedInActionView = true; 126453f56c4f51ae11d5b3afde8ce221b3fe9aea3cf1Adam Powell mCollapsedImeOptions = mQueryTextView.getImeOptions(); 126553f56c4f51ae11d5b3afde8ce221b3fe9aea3cf1Adam Powell mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN); 12668790764e4c20737e57dafdfb3bb1c8cdb84169c5Amith Yamasani mQueryTextView.setText(""); 1267763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani setIconified(false); 1268763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani } 1269763bc076527b183204b8ef82711f9b404bed53dbAmith Yamasani 12708a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov @Override 12718a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 12728a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov super.onInitializeAccessibilityEvent(event); 12738a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov event.setClassName(SearchView.class.getName()); 12748a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 12758a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 12768a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov @Override 12778a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 12788a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov super.onInitializeAccessibilityNodeInfo(info); 12798a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov info.setClassName(SearchView.class.getName()); 12808a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 12818a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 1282b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani private void adjustDropDownSizeAndPosition() { 1283b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (mDropDownAnchor.getWidth() > 1) { 1284b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani Resources res = getContext().getResources(); 1285b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani int anchorPadding = mSearchPlate.getPaddingLeft(); 1286b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani Rect dropDownPadding = new Rect(); 12873a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio final boolean isLayoutRtl = isLayoutRtl(); 1288b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani int iconOffset = mIconifiedByDefault 1289b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani ? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width) 1290b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani + res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left) 1291b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani : 0; 1292b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mQueryTextView.getDropDownBackground().getPadding(dropDownPadding); 12933a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio int offset; 12943a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio if (isLayoutRtl) { 12953a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio offset = - dropDownPadding.left; 12963a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio } else { 12973a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio offset = anchorPadding - (dropDownPadding.left + iconOffset); 12983a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio } 12993a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio mQueryTextView.setDropDownHorizontalOffset(offset); 13003a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio final int width = mDropDownAnchor.getWidth() + dropDownPadding.left 13013a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio + dropDownPadding.right + iconOffset - anchorPadding; 13023a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio mQueryTextView.setDropDownWidth(width); 1303b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1304b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1305b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1306968ec938399033d280b1648123104ac567f2a093Amith Yamasani private boolean onItemClicked(int position, int actionKey, String actionMsg) { 1307968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mOnSuggestionListener == null 130801f21354654a315ea2a2a1668bbda50645123d4aAdam Powell || !mOnSuggestionListener.onSuggestionClick(position)) { 1309968ec938399033d280b1648123104ac567f2a093Amith Yamasani launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); 1310968ec938399033d280b1648123104ac567f2a093Amith Yamasani setImeVisibility(false); 1311968ec938399033d280b1648123104ac567f2a093Amith Yamasani dismissSuggestions(); 1312968ec938399033d280b1648123104ac567f2a093Amith Yamasani return true; 1313968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1314968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 1315968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1316968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1317968ec938399033d280b1648123104ac567f2a093Amith Yamasani private boolean onItemSelected(int position) { 1318968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (mOnSuggestionListener == null 131901f21354654a315ea2a2a1668bbda50645123d4aAdam Powell || !mOnSuggestionListener.onSuggestionSelect(position)) { 1320968ec938399033d280b1648123104ac567f2a093Amith Yamasani rewriteQueryFromSuggestion(position); 1321968ec938399033d280b1648123104ac567f2a093Amith Yamasani return true; 1322968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1323968ec938399033d280b1648123104ac567f2a093Amith Yamasani return false; 1324968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1325968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1326733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { 1327733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1328733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1329733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Implements OnItemClickListener 1330733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1331733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1332968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); 1333968ec938399033d280b1648123104ac567f2a093Amith Yamasani onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null); 1334733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1335733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani }; 1336733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1337733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() { 1338733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1339733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1340733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Implements OnItemSelectedListener 1341733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1342733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 1343968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); 1344968ec938399033d280b1648123104ac567f2a093Amith Yamasani SearchView.this.onItemSelected(position); 1345733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1346733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1347733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1348733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Implements OnItemSelectedListener 1349733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1350733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void onNothingSelected(AdapterView<?> parent) { 1351733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (DBG) 1352733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Log.d(LOG_TAG, "onNothingSelected()"); 1353733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1354733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani }; 1355733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 13563a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio @Override 1357343e11345ed496003f605e1b3bba5850d3e6cf0eFabrice Di Meglio public void onRtlPropertiesChanged(int layoutDirection) { 1358343e11345ed496003f605e1b3bba5850d3e6cf0eFabrice Di Meglio mQueryTextView.setLayoutDirection(layoutDirection); 13593a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio } 13603a69bea30257f32a21bb6c32aafa52982d16a401Fabrice Di Meglio 1361733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1362733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Query rewriting. 1363733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1364733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void rewriteQueryFromSuggestion(int position) { 1365733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani CharSequence oldQuery = mQueryTextView.getText(); 1366733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Cursor c = mSuggestionsAdapter.getCursor(); 1367733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (c == null) { 1368733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return; 1369733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1370733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (c.moveToPosition(position)) { 1371733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // Get the new query from the suggestion. 1372733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani CharSequence newQuery = mSuggestionsAdapter.convertToString(c); 1373733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (newQuery != null) { 1374733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // The suggestion rewrites the query. 1375733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // Update the text field, without getting new suggestions. 1376733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani setQuery(newQuery); 1377733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } else { 1378733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // The suggestion does not rewrite the query, restore the user's query. 1379733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani setQuery(oldQuery); 1380733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1381733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } else { 1382733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // We got a bad position, restore the user's query. 1383733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani setQuery(oldQuery); 1384733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1385733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1386733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1387733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1388733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Launches an intent based on a suggestion. 1389733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 1390733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param position The index of the suggestion to create the intent from. 1391733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionKey The key code of the action key that was pressed, 1392733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1393733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionMsg The message for the action key that was pressed, 1394733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or <code>null</code> if none. 1395733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return true if a successful launch, false if could not (e.g. bad position). 1396733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1397733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private boolean launchSuggestion(int position, int actionKey, String actionMsg) { 1398733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Cursor c = mSuggestionsAdapter.getCursor(); 1399733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if ((c != null) && c.moveToPosition(position)) { 1400733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1401733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); 1402733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1403733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // launch the intent 1404733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani launchIntent(intent); 1405733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1406733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return true; 1407733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1408733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return false; 1409733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1410733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1411733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1412733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Launches an intent, including any special intent handling. 1413733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1414733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void launchIntent(Intent intent) { 1415733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (intent == null) { 1416733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return; 1417733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1418733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani try { 1419733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // If the intent was created from a suggestion, it will always have an explicit 1420733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // component here. 1421733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani getContext().startActivity(intent); 1422733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } catch (RuntimeException ex) { 1423733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Log.e(LOG_TAG, "Failed launch activity: " + intent, ex); 1424733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1425733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1426733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1427733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1428733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Sets the text in the query box, without updating the suggestions. 1429733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1430733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void setQuery(CharSequence query) { 1431e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani mQueryTextView.setText(query, true); 1432e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani // Move the cursor to the end 1433e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length()); 1434733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1435733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1436733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private void launchQuerySearch(int actionKey, String actionMsg, String query) { 1437733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String action = Intent.ACTION_SEARCH; 1438e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani Intent intent = createIntent(action, null, null, query, actionKey, actionMsg); 1439733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani getContext().startActivity(intent); 1440733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1441733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1442733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1443733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Constructs an intent from the given information and the search dialog state. 1444733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 1445733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param action Intent action. 1446733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param data Intent data, or <code>null</code>. 1447733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. 1448733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param query Intent query, or <code>null</code>. 1449733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionKey The key code of the action key that was pressed, 1450733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1451733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionMsg The message for the action key that was pressed, 1452733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or <code>null</code> if none. 1453733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param mode The search mode, one of the acceptable values for 1454733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * {@link SearchManager#SEARCH_MODE}, or {@code null}. 1455733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return The intent. 1456733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1457733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private Intent createIntent(String action, Uri data, String extraData, String query, 1458e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani int actionKey, String actionMsg) { 1459733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // Now build the Intent 1460733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Intent intent = new Intent(action); 1461733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1462733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // We need CLEAR_TOP to avoid reusing an old task that has other activities 1463733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // on top of the one we want. We don't want to do this in in-app search though, 1464733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // as it can be destructive to the activity stack. 1465733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (data != null) { 1466733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.setData(data); 1467733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1468068d73cf51305a9dfc15c96f17a18676637d3e02Amith Yamasani intent.putExtra(SearchManager.USER_QUERY, mUserQuery); 1469733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (query != null) { 1470733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.putExtra(SearchManager.QUERY, query); 1471733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1472733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (extraData != null) { 1473733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); 1474733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1475940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani if (mAppSearchData != null) { 1476940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani intent.putExtra(SearchManager.APP_DATA, mAppSearchData); 1477940ef38c7c46565a8f8fdd6bb8e0183646d0abdcAmith Yamasani } 1478733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { 1479733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.putExtra(SearchManager.ACTION_KEY, actionKey); 1480733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.putExtra(SearchManager.ACTION_MSG, actionMsg); 1481733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1482733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani intent.setComponent(mSearchable.getSearchActivity()); 1483733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return intent; 1484ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1485ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1486ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani /** 1487ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * Create and return an Intent that can launch the voice search activity for web search. 1488ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani */ 1489ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) { 1490ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Intent voiceIntent = new Intent(baseIntent); 1491ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani ComponentName searchActivity = searchable.getSearchActivity(); 1492ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null 1493ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani : searchActivity.flattenToShortString()); 1494ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani return voiceIntent; 1495ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1496ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1497ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani /** 1498ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * Create and return an Intent that can launch the voice search activity, perform a specific 1499ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * voice transcription, and forward the results to the searchable activity. 1500ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * 1501ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * @param baseIntent The voice app search intent to start from 1502ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani * @return A completely-configured intent ready to send to the voice search activity 1503ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani */ 1504ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) { 1505ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani ComponentName searchActivity = searchable.getSearchActivity(); 1506ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1507ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // create the necessary intent to set up a search-and-forward operation 1508ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // in the voice search system. We have to keep the bundle separate, 1509ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // because it becomes immutable once it enters the PendingIntent 1510ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Intent queryIntent = new Intent(Intent.ACTION_SEARCH); 1511ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani queryIntent.setComponent(searchActivity); 1512ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent, 1513ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani PendingIntent.FLAG_ONE_SHOT); 1514ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1515ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Now set up the bundle that will be inserted into the pending intent 1516ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // when it's time to do the search. We always build it here (even if empty) 1517ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // because the voice search activity will always need to insert "QUERY" into 1518ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // it anyway. 1519ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Bundle queryExtras = new Bundle(); 15201bcfe84179417cda57176fe8661a4a749cb843b6Jorge Ruesga if (mAppSearchData != null) { 15211bcfe84179417cda57176fe8661a4a749cb843b6Jorge Ruesga queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData); 15221bcfe84179417cda57176fe8661a4a749cb843b6Jorge Ruesga } 1523ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1524ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Now build the intent to launch the voice search. Add all necessary 1525ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // extras to launch the voice recognizer, and then all the necessary extras 1526ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // to forward the results to the searchable activity 1527ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Intent voiceIntent = new Intent(baseIntent); 1528ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1529ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Add all of the configuration options supplied by the searchable's metadata 1530ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; 1531ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani String prompt = null; 1532ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani String language = null; 1533ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani int maxResults = 1; 1534ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1535ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani Resources resources = getResources(); 1536ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (searchable.getVoiceLanguageModeId() != 0) { 1537ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani languageModel = resources.getString(searchable.getVoiceLanguageModeId()); 1538ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1539ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (searchable.getVoicePromptTextId() != 0) { 1540ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani prompt = resources.getString(searchable.getVoicePromptTextId()); 1541ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1542ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (searchable.getVoiceLanguageId() != 0) { 1543ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani language = resources.getString(searchable.getVoiceLanguageId()); 1544ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1545ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani if (searchable.getVoiceMaxResults() != 0) { 1546ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani maxResults = searchable.getVoiceMaxResults(); 1547ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani } 1548ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); 1549ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); 1550ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); 1551ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); 1552ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null 1553ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani : searchActivity.flattenToShortString()); 1554ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1555ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani // Add the values that configure forwarding the results 1556ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); 1557ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); 1558ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani 1559ebcf5a3a50b84320528af5c2a57db99f76c08af5Amith Yamasani return voiceIntent; 1560733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1561733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1562733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1563733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * When a particular suggestion has been selected, perform the various lookups required 1564733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * to use the suggestion. This includes checking the cursor for suggestion-specific data, 1565733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * and/or falling back to the XML for defaults; It also creates REST style Uri data when 1566733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * the suggestion includes a data id. 1567733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * 1568733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param c The suggestions cursor, moved to the row of the user's selection 1569733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionKey The key code of the action key that was pressed, 1570733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1571733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @param actionMsg The message for the action key that was pressed, 1572733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * or <code>null</code> if none. 1573733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * @return An intent for the suggestion at the cursor's position. 1574733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1575733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) { 1576733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani try { 1577733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // use specific action if supplied, or default action if supplied, or fixed default 1578733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); 1579733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1580733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (action == null) { 1581733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani action = mSearchable.getSuggestIntentAction(); 1582733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1583733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (action == null) { 1584733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani action = Intent.ACTION_SEARCH; 1585733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1586733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1587733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // use specific data if supplied, or default data if supplied 1588733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA); 1589733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (data == null) { 1590733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani data = mSearchable.getSuggestIntentData(); 1591733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1592733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani // then, if an ID was provided, append it. 1593733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (data != null) { 1594733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); 1595733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani if (id != null) { 1596733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani data = data + "/" + Uri.encode(id); 1597733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1598733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1599733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani Uri dataUri = (data == null) ? null : Uri.parse(data); 1600733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1601733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); 1602733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); 1603733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1604e678f46ec45076203f6260f8a26f56d838c6b6ffAmith Yamasani return createIntent(action, dataUri, extraData, query, actionKey, actionMsg); 1605733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } catch (RuntimeException e ) { 1606733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani int rowNum; 1607733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani try { // be really paranoid now 1608733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani rowNum = c.getPosition(); 1609733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } catch (RuntimeException e2 ) { 1610733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani rowNum = -1; 1611733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 161273af45169476663ead1adcf1717dddd9ad4658fdJake Wharton Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum + 161373af45169476663ead1adcf1717dddd9ad4658fdJake Wharton " returned exception.", e); 1614733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani return null; 1615733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1616733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1617733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1618f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani private void forceSuggestionQuery() { 1619f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani mQueryTextView.doBeforeTextChanged(); 1620f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani mQueryTextView.doAfterTextChanged(); 1621f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani } 1622f28d1875741d1845df45808e143f3a850e66f893Amith Yamasani 1623968ec938399033d280b1648123104ac567f2a093Amith Yamasani static boolean isLandscapeMode(Context context) { 1624968ec938399033d280b1648123104ac567f2a093Amith Yamasani return context.getResources().getConfiguration().orientation 1625968ec938399033d280b1648123104ac567f2a093Amith Yamasani == Configuration.ORIENTATION_LANDSCAPE; 1626968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1627968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1628733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani /** 1629733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani * Callback to watch the text field for empty/non-empty 1630733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani */ 1631733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani private TextWatcher mTextWatcher = new TextWatcher() { 1632733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1633733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void beforeTextChanged(CharSequence s, int start, int before, int after) { } 1634733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1635733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void onTextChanged(CharSequence s, int start, 1636733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani int before, int after) { 1637733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani SearchView.this.onTextChanged(s); 1638733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1639733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani 1640733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani public void afterTextChanged(Editable s) { 1641733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani } 1642733cbd58cbc3cf438a7ac0a2902b8aeab941a6b9Amith Yamasani }; 1643968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1644968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1645968ec938399033d280b1648123104ac567f2a093Amith Yamasani * Local subclass for AutoCompleteTextView. 1646968ec938399033d280b1648123104ac567f2a093Amith Yamasani * @hide 1647968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1648968ec938399033d280b1648123104ac567f2a093Amith Yamasani public static class SearchAutoComplete extends AutoCompleteTextView { 1649968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1650968ec938399033d280b1648123104ac567f2a093Amith Yamasani private int mThreshold; 1651968ec938399033d280b1648123104ac567f2a093Amith Yamasani private SearchView mSearchView; 1652968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1653968ec938399033d280b1648123104ac567f2a093Amith Yamasani public SearchAutoComplete(Context context) { 1654968ec938399033d280b1648123104ac567f2a093Amith Yamasani super(context); 1655968ec938399033d280b1648123104ac567f2a093Amith Yamasani mThreshold = getThreshold(); 1656968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1657968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1658968ec938399033d280b1648123104ac567f2a093Amith Yamasani public SearchAutoComplete(Context context, AttributeSet attrs) { 1659968ec938399033d280b1648123104ac567f2a093Amith Yamasani super(context, attrs); 1660968ec938399033d280b1648123104ac567f2a093Amith Yamasani mThreshold = getThreshold(); 1661968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1662968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1663968ec938399033d280b1648123104ac567f2a093Amith Yamasani public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { 1664968ec938399033d280b1648123104ac567f2a093Amith Yamasani super(context, attrs, defStyle); 1665968ec938399033d280b1648123104ac567f2a093Amith Yamasani mThreshold = getThreshold(); 1666968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1667968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1668968ec938399033d280b1648123104ac567f2a093Amith Yamasani void setSearchView(SearchView searchView) { 1669968ec938399033d280b1648123104ac567f2a093Amith Yamasani mSearchView = searchView; 1670968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1671968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1672968ec938399033d280b1648123104ac567f2a093Amith Yamasani @Override 1673968ec938399033d280b1648123104ac567f2a093Amith Yamasani public void setThreshold(int threshold) { 1674968ec938399033d280b1648123104ac567f2a093Amith Yamasani super.setThreshold(threshold); 1675968ec938399033d280b1648123104ac567f2a093Amith Yamasani mThreshold = threshold; 1676968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1677968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1678968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1679968ec938399033d280b1648123104ac567f2a093Amith Yamasani * Returns true if the text field is empty, or contains only whitespace. 1680968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1681968ec938399033d280b1648123104ac567f2a093Amith Yamasani private boolean isEmpty() { 1682968ec938399033d280b1648123104ac567f2a093Amith Yamasani return TextUtils.getTrimmedLength(getText()) == 0; 1683968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1684968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1685968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1686968ec938399033d280b1648123104ac567f2a093Amith Yamasani * We override this method to avoid replacing the query box text when a 1687968ec938399033d280b1648123104ac567f2a093Amith Yamasani * suggestion is clicked. 1688968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1689968ec938399033d280b1648123104ac567f2a093Amith Yamasani @Override 1690968ec938399033d280b1648123104ac567f2a093Amith Yamasani protected void replaceText(CharSequence text) { 1691968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1692968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1693968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1694968ec938399033d280b1648123104ac567f2a093Amith Yamasani * We override this method to avoid an extra onItemClick being called on 1695968ec938399033d280b1648123104ac567f2a093Amith Yamasani * the drop-down's OnItemClickListener by 1696968ec938399033d280b1648123104ac567f2a093Amith Yamasani * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is 1697968ec938399033d280b1648123104ac567f2a093Amith Yamasani * clicked with the trackball. 1698968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1699968ec938399033d280b1648123104ac567f2a093Amith Yamasani @Override 1700968ec938399033d280b1648123104ac567f2a093Amith Yamasani public void performCompletion() { 1701968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1702968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1703968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1704968ec938399033d280b1648123104ac567f2a093Amith Yamasani * We override this method to be sure and show the soft keyboard if 1705968ec938399033d280b1648123104ac567f2a093Amith Yamasani * appropriate when the TextView has focus. 1706968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1707968ec938399033d280b1648123104ac567f2a093Amith Yamasani @Override 1708968ec938399033d280b1648123104ac567f2a093Amith Yamasani public void onWindowFocusChanged(boolean hasWindowFocus) { 1709968ec938399033d280b1648123104ac567f2a093Amith Yamasani super.onWindowFocusChanged(hasWindowFocus); 1710968ec938399033d280b1648123104ac567f2a093Amith Yamasani 1711acd8d2d2e7b36c9fb34c2981623d19247fa63860Amith Yamasani if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) { 1712968ec938399033d280b1648123104ac567f2a093Amith Yamasani InputMethodManager inputManager = (InputMethodManager) getContext() 1713968ec938399033d280b1648123104ac567f2a093Amith Yamasani .getSystemService(Context.INPUT_METHOD_SERVICE); 1714968ec938399033d280b1648123104ac567f2a093Amith Yamasani inputManager.showSoftInput(this, 0); 1715968ec938399033d280b1648123104ac567f2a093Amith Yamasani // If in landscape mode, then make sure that 1716968ec938399033d280b1648123104ac567f2a093Amith Yamasani // the ime is in front of the dropdown. 1717968ec938399033d280b1648123104ac567f2a093Amith Yamasani if (isLandscapeMode(getContext())) { 1718968ec938399033d280b1648123104ac567f2a093Amith Yamasani ensureImeVisible(true); 1719968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1720968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1721968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1722968ec938399033d280b1648123104ac567f2a093Amith Yamasani 17234aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani @Override 17244aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 17254aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani super.onFocusChanged(focused, direction, previouslyFocusedRect); 17264aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani mSearchView.onTextFocusChanged(); 17274aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani } 17284aedb39a49bda340f871c8fac2239de4fe549b03Amith Yamasani 1729968ec938399033d280b1648123104ac567f2a093Amith Yamasani /** 1730968ec938399033d280b1648123104ac567f2a093Amith Yamasani * We override this method so that we can allow a threshold of zero, 1731968ec938399033d280b1648123104ac567f2a093Amith Yamasani * which ACTV does not. 1732968ec938399033d280b1648123104ac567f2a093Amith Yamasani */ 1733968ec938399033d280b1648123104ac567f2a093Amith Yamasani @Override 1734968ec938399033d280b1648123104ac567f2a093Amith Yamasani public boolean enoughToFilter() { 1735968ec938399033d280b1648123104ac567f2a093Amith Yamasani return mThreshold <= 0 || super.enoughToFilter(); 1736968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 1737b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1738b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani @Override 1739b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani public boolean onKeyPreIme(int keyCode, KeyEvent event) { 1740b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (keyCode == KeyEvent.KEYCODE_BACK) { 1741b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // special case for the back key, we do not even try to send it 1742b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani // to the drop down list but instead, consume it immediately 1743b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 1744b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani KeyEvent.DispatcherState state = getKeyDispatcherState(); 1745b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (state != null) { 1746b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani state.startTracking(event, this); 1747b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1748b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani return true; 1749b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } else if (event.getAction() == KeyEvent.ACTION_UP) { 1750b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani KeyEvent.DispatcherState state = getKeyDispatcherState(); 1751b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (state != null) { 1752b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani state.handleUpEvent(event); 1753b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1754b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani if (event.isTracking() && !event.isCanceled()) { 1755b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mSearchView.clearFocus(); 1756b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani mSearchView.setImeVisibility(false); 1757b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani return true; 1758b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1759b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1760b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1761b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani return super.onKeyPreIme(keyCode, event); 1762b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani } 1763b4569fb17fada4fdc43e4f4dbfbc79bb097a1f74Amith Yamasani 1764968ec938399033d280b1648123104ac567f2a093Amith Yamasani } 17650594476ffa39481063cf7f1546ccfce509f02c65Amith Yamasani} 1766