SearchDialog.java revision dc1396c881b80074c99d171aee02d3a5486180ca
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.app; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport static android.app.SuggestionsAdapter.getColumnString; 20c1f40960644b461b108480b6446c89963441492dBjorn Bringert 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ActivityNotFoundException; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ComponentName; 23d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaenimport android.content.ContentResolver; 24d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaenimport android.content.ContentValues; 25444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringertimport android.content.Context; 26444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringertimport android.content.Intent; 27875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.pm.ActivityInfo; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.PackageManager; 2998e333f551a4bf2ebb50bb97a2a56b14bfdcd74bKarl Rosaenimport android.content.pm.ResolveInfo; 30c1f40960644b461b108480b6446c89963441492dBjorn Bringertimport android.content.pm.PackageManager.NameNotFoundException; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor; 33444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringertimport android.graphics.drawable.Drawable; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri; 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Bundle; 364899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringertimport android.os.IBinder; 374899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringertimport android.os.RemoteException; 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock; 394899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringertimport android.provider.Browser; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.server.search.SearchableInfo; 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.speech.RecognizerIntent; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Editable; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType; 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils; 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextWatcher; 46662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampathimport android.text.util.Regex; 474899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringertimport android.util.AndroidRuntimeException; 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet; 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 50a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeauimport android.view.ContextThemeWrapper; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Gravity; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.KeyEvent; 53875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.view.MotionEvent; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View; 55875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.view.ViewConfiguration; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewGroup; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Window; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager; 59662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampathimport android.view.inputmethod.EditorInfo; 60a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackbornimport android.view.inputmethod.InputMethodManager; 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.AdapterView; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.AutoCompleteTextView; 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.Button; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.ImageButton; 651fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeauimport android.widget.ImageView; 66077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringertimport android.widget.LinearLayout; 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.ListView; 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView; 69c1f40960644b461b108480b6446c89963441492dBjorn Bringertimport android.widget.AdapterView.OnItemClickListener; 70c1f40960644b461b108480b6446c89963441492dBjorn Bringertimport android.widget.AdapterView.OnItemSelectedListener; 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 72875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport java.util.ArrayList; 73875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport java.util.WeakHashMap; 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.atomic.AtomicLong; 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 778d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert * System search dialog. This is controlled by the 788d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert * SearchManagerService and runs in the system process. 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class SearchDialog extends Dialog implements OnItemClickListener, OnItemSelectedListener { 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Debugging support 85875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private static final boolean DBG = false; 86875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private static final String LOG_TAG = "SearchDialog"; 87875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private static final boolean DBG_LOG_TIMING = false; 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String INSTANCE_KEY_COMPONENT = "comp"; 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String INSTANCE_KEY_APPDATA = "data"; 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String INSTANCE_KEY_GLOBALSEARCH = "glob"; 92b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp"; 93b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert private static final String INSTANCE_KEY_STORED_APPDATA = "sData"; 94b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev"; 95b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert private static final String INSTANCE_KEY_USER_QUERY = "uQry"; 96260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau 97260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau // The extra key used in an intent to the speech recognizer for in-app voice search. 98260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau private static final String EXTRA_CALLING_PACKAGE = "calling_package"; 997df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau 1007df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau // The string used for privateImeOptions to identify to the IME that it should not show 1017df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau // a microphone button since one already exists in the search dialog. 1027df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau private static final String IME_OPTION_NO_MICROPHONE = "nm"; 103b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert 1041fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12; 1051fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7; 106444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // views & widgets 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextView mBadgeLabel; 1091fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau private ImageView mAppIcon; 110875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private SearchAutoComplete mSearchAutoComplete; 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Button mGoButton; 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private ImageButton mVoiceButton; 113875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private View mSearchPlate; 114f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy private Drawable mWorkingSpinner; 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // interaction with searchable application 117875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private SearchableInfo mSearchable; 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private ComponentName mLaunchComponent; 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Bundle mAppSearchData; 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mGlobalSearchMode; 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Context mActivityContext; 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 123b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau // Values we store to allow user to toggle between in-app search and global search. 124b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau private ComponentName mStoredComponentName; 125b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau private Bundle mStoredAppSearchData; 126b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau 127875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // stack of previous searchables, to support the BACK key after 128875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE. 129875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The top of the stack (= previous searchable) is the last element of the list, 130875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // since adding and removing is efficient at the end of an ArrayList. 131875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private ArrayList<ComponentName> mPreviousComponents; 132875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // For voice searching 134f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney private final Intent mVoiceWebSearchIntent; 135f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney private final Intent mVoiceAppSearchIntent; 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // support for AutoCompleteTextView suggestions display 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private SuggestionsAdapter mSuggestionsAdapter; 139875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 140875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Whether to rewrite queries when selecting suggestions 141cce7dbc31ed100b39c3e1174dd3d9a01de648e5bMike LeBeau private static final boolean REWRITE_QUERIES = true; 142875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 143875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The query entered by the user. This is not changed when selecting a suggestion 144875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // that modifies the contents of the text field. But if the user then edits 145875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // the suggestion, the resulting string is saved. 146875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private String mUserQuery; 147875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 148875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // A weak map of drawables we've gotten from other packages, so we don't load them 149875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // more than once. 150dfefa3e915938aa1eeeaa7c0910a882bded7bcf7Bjorn Bringert private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache = 151dfefa3e915938aa1eeeaa7c0910a882bded7bcf7Bjorn Bringert new WeakHashMap<String, Drawable.ConstantState>(); 152662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 153662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath // Last known IME options value for the search edit text. 154662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath private int mSearchAutoCompleteImeOptions; 155662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Constructor - fires it up and makes it look like the search UI. 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context Application Context we can use for system acess 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SearchDialog(Context context) { 162a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau super(context, com.android.internal.R.style.Theme_GlobalSearchBar); 163f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney 164f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney // Save voice intent for later queries/launching 165f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 166f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 167f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 168f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 169f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney 170f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 171f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 175f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney * Create the search dialog and any resources that are used for the 176f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney * entire lifetime of the dialog. 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected void onCreate(Bundle savedInstanceState) { 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onCreate(savedInstanceState); 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1828d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert Window theWindow = getWindow(); 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project WindowManager.LayoutParams lp = theWindow.getAttributes(); 1848d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR; 1858d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert lp.width = ViewGroup.LayoutParams.FILL_PARENT; 1868d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert // taking up the whole window (even when transparent) is less than ideal, 1878d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert // but necessary to show the popup window until the window manager supports 1888d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert // having windows anchored by their parent but not clipped by them. 1898d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert lp.height = ViewGroup.LayoutParams.FILL_PARENT; 1908d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; 19198acd54fa6657e8131b3a2a957de7882a2f511abMike LeBeau lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project theWindow.setAttributes(lp); 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 194f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney // Touching outside of the search dialog will dismiss it 195f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney setCanceledOnTouchOutside(true); 196f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney } 197f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney 198f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney /** 199f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney * We recreate the dialog view each time it becomes visible so as to limit 200f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney * the scope of any problems with the contained resources. 201f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney */ 202f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney private void createContentView() { 203f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney setContentView(com.android.internal.R.layout.search_bar); 204f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // get the view elements for local access 206077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar); 207077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert searchBar.setSearchDialog(this); 208077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge); 210875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete = (SearchAutoComplete) 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findViewById(com.android.internal.R.id.search_src_text); 2121fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon); 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); 215875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchPlate = findViewById(com.android.internal.R.id.search_plate); 216f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy mWorkingSpinner = getContext().getResources(). 2171480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau getDrawable(com.android.internal.R.drawable.search_spinner); 218dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( 219dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney null, null, mWorkingSpinner, null); 220dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney setWorking(false); 221dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // attach listeners 223875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.addTextChangedListener(mTextWatcher); 224875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setOnKeyListener(mTextKeyListener); 225875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setOnItemClickListener(this); 226875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setOnItemSelectedListener(this); 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton.setOnClickListener(mGoButtonClickListener); 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton.setOnKeyListener(mButtonsKeyListener); 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVoiceButton.setOnClickListener(mVoiceButtonClickListener); 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVoiceButton.setOnKeyListener(mButtonsKeyListener); 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // pre-hide all the extraneous elements 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBadgeLabel.setVisibility(View.GONE); 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Additional adjustments to make Dialog work for Search 236662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions(); 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Set up the search dialog 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 242875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return true if search dialog launched, false if not 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean show(String initialQuery, boolean selectInitialQuery, 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ComponentName componentName, Bundle appSearchData, boolean globalSearch) { 2468d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert 247b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau // Reset any stored values from last time dialog was shown. 248b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau mStoredComponentName = null; 249b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau mStoredAppSearchData = null; 250fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath 251fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData, 252fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath globalSearch); 253fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath if (success) { 254fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath // Display the drop down as soon as possible instead of waiting for the rest of the 255fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath // pending UI stuff to get done, so that things appear faster to the user. 256fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath mSearchAutoComplete.showDropDownAfterLayout(); 257fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath } 258fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath return success; 259b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } 260da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert 261da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert private boolean isInRealAppSearch() { 262da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert return !mGlobalSearchMode 263da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert && (mPreviousComponents == null || mPreviousComponents.isEmpty()); 264da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert } 265da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert 266b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau /** 267b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * Called in response to a press of the hard search button in 268b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app 269b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * search and global search when relevant. 270b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * 271b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * If pressed within an in-app search context, this switches the search dialog out to 272b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * global search. If pressed within a global search context that was originally an in-app 273b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * search context, this switches back to the in-app search context. If pressed within a 274b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * global search context that has no original in-app search context (e.g., global search 275b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * from Home), this does nothing. 276b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * 277b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * @return false if we wanted to toggle context but could not do so successfully, true 278b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * in all other cases 279b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau */ 280b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau private boolean toggleGlobalSearch() { 281b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau String currentSearchText = mSearchAutoComplete.getText().toString(); 282b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau if (!mGlobalSearchMode) { 283b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau mStoredComponentName = mLaunchComponent; 284b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau mStoredAppSearchData = mAppSearchData; 2855a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau 2865a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // If this is the browser, we have a special case to not show the icon to the left 2875a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // of the text field, for extra space for url entry (this should be reconciled in 2885a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // Eclair). So special case a second tap of the search button to remove any 2895a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // already-entered text so that we can be sure to show the "Quick Search Box" hint 2905a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // text to still make it clear to the user that we've jumped out to global search. 2915a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // 2925a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // TODO: When the browser icon issue is reconciled in Eclair, remove this special case. 2935a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau if (isBrowserSearch()) currentSearchText = ""; 2945a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau 295b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau return doShow(currentSearchText, false, null, mAppSearchData, true); 296b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } else { 297b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau if (mStoredComponentName != null) { 298b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau // This means we should toggle *back* to an in-app search context from 299b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau // global search. 300b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau return doShow(currentSearchText, false, mStoredComponentName, 301b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau mStoredAppSearchData, false); 302b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } else { 303b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau return true; 304b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } 305b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } 306b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau } 307b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau 308b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau /** 309b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * Does the rest of the work required to show the search dialog. Called by both 310b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * {@link #show(String, boolean, ComponentName, Bundle, boolean)} and 311b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * {@link #toggleGlobalSearch()}. 312b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * 313b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau * @return true if search dialog showed, false if not 314b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau */ 315b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau private boolean doShow(String initialQuery, boolean selectInitialQuery, 316b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau ComponentName componentName, Bundle appSearchData, 317b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau boolean globalSearch) { 318875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // set up the searchable and show the dialog 319875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!show(componentName, appSearchData, globalSearch)) { 320875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 321875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 322b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau 323875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // finally, load the user's initial text (which may trigger suggestions) 324875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen setUserQuery(initialQuery); 325875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (selectInitialQuery) { 326875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.selectAll(); 327875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 328b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau 329875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 330875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 331b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau 332875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 333875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Sets up the search dialog and shows it. 334875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 335875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return <code>true</code> if search dialog launched 336875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 337875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private boolean show(ComponentName componentName, Bundle appSearchData, 338875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen boolean globalSearch) { 339875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 340875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) { 341875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.d(LOG_TAG, "show(" + componentName + ", " 342875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen + appSearchData + ", " + globalSearch + ")"); 3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 344875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 3458d1538237847baf381787b881141f8c0478bef5bBjorn Bringert SearchManager searchManager = (SearchManager) 3468d1538237847baf381787b881141f8c0478bef5bBjorn Bringert mContext.getSystemService(Context.SEARCH_SERVICE); 347a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau // Try to get the searchable info for the provided component (or for global search, 348a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau // if globalSearch == true). 3498d1538237847baf381787b881141f8c0478bef5bBjorn Bringert mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); 350a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau 351a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau // If we got back nothing, and it wasn't a request for global search, then try again 352a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau // for global search, as we'll try to launch that in lieu of any component-specific search. 353a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau if (!globalSearch && mSearchable == null) { 354a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau globalSearch = true; 3558d1538237847baf381787b881141f8c0478bef5bBjorn Bringert mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 357ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert 358ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert // If there's not even a searchable info available for global search, then really give up. 359ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert if (mSearchable == null) { 360ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert Log.w(LOG_TAG, "No global search provider."); 361ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert return false; 362ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert } 363ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert 3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLaunchComponent = componentName; 3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAppSearchData = appSearchData; 366875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Using globalSearch here is just an optimization, just calling 367875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // isDefaultSearchable() should always give the same result. 3688d1538237847baf381787b881141f8c0478bef5bBjorn Bringert mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable); 369875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mActivityContext = mSearchable.getActivityContext(getContext()); 370f36740b2e4a9a187bf688c632c1d0a7ae926c96aBryan Mawhinney 371875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // show the dialog. this will call onStart(). 37211f09bb5cd45a76ae7757b6f20807e836c3692f8Bjorn Bringert if (!isShowing()) { 37311f09bb5cd45a76ae7757b6f20807e836c3692f8Bjorn Bringert // Recreate the search bar view every time the dialog is shown, to get rid 37411f09bb5cd45a76ae7757b6f20807e836c3692f8Bjorn Bringert // of any bad state in the AutoCompleteTextView etc 37511f09bb5cd45a76ae7757b6f20807e836c3692f8Bjorn Bringert createContentView(); 37611f09bb5cd45a76ae7757b6f20807e836c3692f8Bjorn Bringert 377a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau // The Dialog uses a ContextThemeWrapper for the context; use this to change the 378a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau // theme out from underneath us, between the global search theme and the in-app 379a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau // search theme. They are identical except that the global search theme does not 380a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau // dim the background of the window (because global search is full screen so it's 381a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau // not needed and this should save a little bit of time on global search invocation). 382a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau Object context = getContext(); 383a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau if (context instanceof ContextThemeWrapper) { 384a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau ContextThemeWrapper wrapper = (ContextThemeWrapper) context; 385a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau if (globalSearch) { 386a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar); 387a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau } else { 388a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar); 389a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau } 390a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau } 391875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen show(); 3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 393875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen updateUI(); 3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The search dialog is being dismissed, so handle all of the local shutdown operations. 4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This function is designed to be idempotent so that dismiss() can be safely called at any time 4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * (even if already closed) and more likely to really dump any memory. No leaks! 4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onStop() { 4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onStop(); 407444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 408875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen closeSuggestionsAdapter(); 4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // dump extra memory we're hanging on to 4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLaunchComponent = null; 4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAppSearchData = null; 4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSearchable = null; 4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mActivityContext = null; 4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mUserQuery = null; 416875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mPreviousComponents = null; 417875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 4181c6907515134c8a2e086d2eed4427f909d301935Mike LeBeau 4191c6907515134c8a2e086d2eed4427f909d301935Mike LeBeau /** 4201480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau * Sets the search dialog to the 'working' state, which shows a working spinner in the 4211480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau * right hand size of the text field. 4221480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau * 4231480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau * @param working true to show spinner, false to hide spinner 4241480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau */ 4251480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau public void setWorking(boolean working) { 426dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney mWorkingSpinner.setAlpha(working ? 255 : 0); 427dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney mWorkingSpinner.setVisible(working, false); 428dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney mWorkingSpinner.invalidateSelf(); 4291480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau } 4301480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau 4311480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau /** 432875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Closes and gets rid of the suggestions adapter. 433875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 434875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void closeSuggestionsAdapter() { 435875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // remove the adapter from the autocomplete first, to avoid any updates 436875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // when we drop the cursor 437875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setAdapter((SuggestionsAdapter)null); 438875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // close any leftover cursor 439875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSuggestionsAdapter != null) { 440ba223760895e62ad2fcb85476984cb29018860c9Bjorn Bringert mSuggestionsAdapter.close(); 441875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 442875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSuggestionsAdapter = null; 4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Save the minimal set of data necessary to recreate the search 4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 448444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert * @return A bundle with the state of the dialog, or {@code null} if the search 449444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert * dialog is not showing. 4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Bundle onSaveInstanceState() { 453444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert if (!isShowing()) return null; 454444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Bundle bundle = new Bundle(); 456444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // setup info so I can recreate this particular search 4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent); 4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData); 4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode); 461b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName); 462b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData); 463b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents); 464b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery); 465b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return bundle; 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Restore the state of the dialog from a previously saved bundle. 471875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 472875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * TODO: go through this and make sure that it saves everything that is saved 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param savedInstanceState The state of the dialog previously saved by 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #onSaveInstanceState()}. 4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onRestoreInstanceState(Bundle savedInstanceState) { 479444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert if (savedInstanceState == null) return; 480444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT); 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA); 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH); 484b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert ComponentName storedComponentName = 485b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT); 486b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert Bundle storedAppSearchData = 487b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA); 488b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert ArrayList<ComponentName> previousComponents = 489b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS); 490b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY); 491b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert 492b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert // Set stored state 493b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert mStoredComponentName = storedComponentName; 494b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert mStoredAppSearchData = storedAppSearchData; 495b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert mPreviousComponents = previousComponents; 496b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert 497b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert // show the dialog. 498b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) { 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // for some reason, we couldn't re-instantiate 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 505875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Called after resources have changed, e.g. after screen rotation or locale change. 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 507444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert public void onConfigurationChanged() { 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (isShowing()) { 5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Redraw (resources may have changed) 5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateSearchButton(); 5111fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau updateSearchAppIcon(); 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateSearchBadge(); 5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateQueryHint(); 51429d52e120f36b46d7ef596b1c182db52d3476be7Bryan Mawhinney mSearchAutoComplete.showDropDownAfterLayout(); 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 517875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 519875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Update the UI according to the info in the current value of {@link #mSearchable}. 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 521875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void updateUI() { 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable != null) { 523ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen mDecor.setVisibility(View.VISIBLE); 524875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen updateSearchAutoComplete(); 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateSearchButton(); 5261fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau updateSearchAppIcon(); 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateSearchBadge(); 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateQueryHint(); 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateVoiceButton(); 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // In order to properly configure the input method (if one is being used), we 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // need to let it know if we'll be providing suggestions. Although it would be 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // difficult/expensive to know if every last detail has been configured properly, we 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // can at least see if a suggestions provider has been configured, and use that 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // as our trigger. 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int inputType = mSearchable.getInputType(); 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We only touch this if the input type is set up for text (which it almost certainly 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // should be, in the case of search!) 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) { 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The existence of a suggestions authority is the proxy for "suggestions 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // are available here" 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getSuggestAuthority() != null) { 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 547875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setInputType(inputType); 548662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoCompleteImeOptions = mSearchable.getImeOptions(); 549662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions); 5507df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau 5517df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau // If the search dialog is going to show a voice search button, then don't let 5527df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau // the soft keyboard display a microphone button if it would have otherwise. 5537df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau if (mSearchable.getVoiceSearchEnabled()) { 5547df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau mSearchAutoComplete.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE); 5557df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau } else { 5567df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau mSearchAutoComplete.setPrivateImeOptions(null); 5577df846186d29ddfa4a567deddd04ad1043d32fd8Mike LeBeau } 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 560875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 562875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Updates the auto-complete text view. 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 564875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void updateSearchAutoComplete() { 565875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // close any existing suggestions adapter 566875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen closeSuggestionsAdapter(); 567875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 568875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation 569203464af9638e4d5aecd811452af67fff9aa3087Bjorn Bringert mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold()); 5708d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert // we dismiss the entire dialog instead 5718d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert mSearchAutoComplete.setDropDownDismissedOnCompletion(false); 572875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 573da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert if (!isInRealAppSearch()) { 574875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in 575875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } else { 576875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setDropDownAlwaysVisible(false); 577875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 578875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 579d4760d77e7e8ab66af0307dcae39e0a8e9d90a14Mike LeBeau mSearchAutoComplete.setForceIgnoreOutsideTouch(true); 580d4760d77e7e8ab66af0307dcae39e0a8e9d90a14Mike LeBeau 581875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // attach the suggestions adapter, if suggestions are available 582875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The existence of a suggestions authority is the proxy for "suggestions available here" 583875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable.getSuggestAuthority() != null) { 5841480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable, 5851480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau mOutsideDrawablesCache, mGlobalSearchMode); 586875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setAdapter(mSuggestionsAdapter); 587875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 589875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Update the text in the search button. Note: This is deprecated functionality, for 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1.0 compatibility only. 5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateSearchButton() { 5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String textLabel = null; 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Drawable iconLabel = null; 5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int textId = mSearchable.getSearchButtonText(); 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (textId != 0) { 5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project textLabel = mActivityContext.getResources().getString(textId); 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project iconLabel = getContext().getResources(). 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getDrawable(com.android.internal.R.drawable.ic_btn_search); 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 604875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mGoButton.setText(textLabel); 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null); 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6081fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau private void updateSearchAppIcon() { 6095a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // In Donut, we special-case the case of the browser to hide the app icon as if it were 6105a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // global search, for extra space for url entry. 6115a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // 6125a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau // TODO: Remove this special case once the issue has been reconciled in Eclair. 6135a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau if (mGlobalSearchMode || isBrowserSearch()) { 6141fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mAppIcon.setImageResource(0); 6151fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mAppIcon.setVisibility(View.GONE); 6161fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL, 6171fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingTop(), 6181fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingRight(), 6191fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingBottom()); 6201fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau } else { 6211fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau PackageManager pm = getContext().getPackageManager(); 622f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy Drawable icon; 6231fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau try { 6241fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); 6251fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau icon = pm.getApplicationIcon(info.applicationInfo); 6261fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau if (DBG) Log.d(LOG_TAG, "Using app-specific icon"); 6271fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau } catch (NameNotFoundException e) { 6281fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau icon = pm.getDefaultActivityIcon(); 6291fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon"); 6301fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau } 6311fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mAppIcon.setImageDrawable(icon); 6321fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mAppIcon.setVisibility(View.VISIBLE); 6331fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, 6341fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingTop(), 6351fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingRight(), 6361fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau mSearchPlate.getPaddingBottom()); 6371fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau } 6381fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau } 6391fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 641875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Setup the search "Badge" if requested by mode flags. 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateSearchBadge() { 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // assume both hidden 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int visibility = View.GONE; 6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Drawable icon = null; 647875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen CharSequence text = null; 6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // optionally show one or the other. 650a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert if (mSearchable.useBadgeIcon()) { 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId()); 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project visibility = View.VISIBLE; 653875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId()); 654a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert } else if (mSearchable.useBadgeLabel()) { 6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString(); 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project visibility = View.VISIBLE; 657875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId()); 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBadgeLabel.setText(text); 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBadgeLabel.setVisibility(visibility); 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Update the hint in the query text field. 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateQueryHint() { 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (isShowing()) { 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String hint = null; 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable != null) { 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int hintId = mSearchable.getHintId(); 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (hintId != 0) { 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project hint = mActivityContext.getString(hintId); 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 677875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setHint(hint); 6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Update the visibility of the voice button. There are actually two voice search modes, 6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * either of which will activate the button. 6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateVoiceButton() { 6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int visibility = View.GONE; 6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceSearchEnabled()) { 6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Intent testIntent = null; 6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceSearchLaunchWebSearch()) { 6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project testIntent = mVoiceWebSearchIntent; 6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { 6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project testIntent = mVoiceAppSearchIntent; 6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (testIntent != null) { 6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ResolveInfo ri = getContext().getPackageManager(). 6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY); 6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ri != null) { 6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project visibility = View.VISIBLE; 6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVoiceButton.setVisibility(visibility); 7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7045a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau 7055a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau /** 7065a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau * Hack to determine whether this is the browser, so we can remove the browser icon 7075a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau * to the left of the search field, as a special requirement for Donut. 7085a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau * 7095a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau * TODO: For Eclair, reconcile this with the rest of the global search UI. 7105a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau */ 7115a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau private boolean isBrowserSearch() { 7125a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/"); 7135a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau } 714f4acf39d99d105e1e4fc7c43fa54377bb2f262aeBjorn Bringert 7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Listeners of various types 7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 720875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * {@link Dialog#onTouchEvent(MotionEvent)} will cancel the dialog only when the 721875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * touch is outside the window. But the window includes space for the drop-down, 722875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * so we also cancel on taps outside the search bar when the drop-down is not showing. 723875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 724875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen @Override 725875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen public boolean onTouchEvent(MotionEvent event) { 726875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // cancel if the drop-down is not showing and the touch event was outside the search plate 727875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!mSearchAutoComplete.isPopupShowing() && isOutOfBounds(mSearchPlate, event)) { 728875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Pop-up not showing and outside of search plate."); 729875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen cancel(); 730875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 731875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 732875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Let Dialog handle events outside the window while the pop-up is showing. 733875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return super.onTouchEvent(event); 734875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 735875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 736875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private boolean isOutOfBounds(View v, MotionEvent event) { 737875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen final int x = (int) event.getX(); 738875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen final int y = (int) event.getY(); 739875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); 740875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return (x < -slop) || (y < -slop) 741875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen || (x > (v.getWidth()+slop)) 742875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen || (y > (v.getHeight()+slop)); 743875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 744875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 745875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Dialog's OnKeyListener implements various search-specific functionality 7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param keyCode This is the keycode of the typed key, and is the same value as 749875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * found in the KeyEvent parameter. 7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param event The complete event record for the typed key 7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return Return true if the event was handled here, or false if not. 7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKeyDown(int keyCode, KeyEvent event) { 756875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "onKeyDown(" + keyCode + "," + event + ")"); 757ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert if (mSearchable == null) { 758ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert return false; 759ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert } 760ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert 7613c5767d8fea785ec8d4ad6de96032cdfe752f5daBryan Mawhinney if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) { 7623c5767d8fea785ec8d4ad6de96032cdfe752f5daBryan Mawhinney event.startTracking(); 76383fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn // Consume search key for later use. 76483fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn return true; 7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 766875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 767875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // if it's an action specified by the searchable activity, launch the 768875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // entered query with the action key 769875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 770a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { 771a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert launchQuerySearch(keyCode, actionKey.getQueryActionMsg()); 772875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 773875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 77487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 77587d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert return super.onKeyDown(keyCode, event); 7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 777875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 77883fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn @Override 77983fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn public boolean onKeyUp(int keyCode, KeyEvent event) { 78083fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn if (DBG) Log.d(LOG_TAG, "onKeyUp(" + keyCode + "," + event + ")"); 78183fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn if (mSearchable == null) { 78283fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn return false; 78383fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn } 78483fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn 78583fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking() 78683fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn && !event.isCanceled()) { 78783fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn // If the search key is pressed, toggle between global and in-app search. If we are 78883fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn // currently doing global search and there is no in-app search context to toggle to, 78983fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn // just don't do anything. 79083fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn return toggleGlobalSearch(); 79183fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn } 79287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 79387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert return super.onKeyUp(keyCode, event); 79483fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn } 79583fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Callback to watch the textedit field for empty/non-empty 7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private TextWatcher mTextWatcher = new TextWatcher() { 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 801875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen public void beforeTextChanged(CharSequence s, int start, int before, int after) { } 8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onTextChanged(CharSequence s, int start, 8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int before, int after) { 805875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG_LOG_TIMING) { 8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dbgLogTiming("onTextChanged()"); 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 808ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert if (mSearchable == null) { 809ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert return; 810ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert } 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateWidgetState(); 812875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!mSearchAutoComplete.isPerformingCompletion()) { 813875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The user changed the query, remember it. 814875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mUserQuery = s == null ? "" : s.toString(); 8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 818662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath public void afterTextChanged(Editable s) { 819b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn if (mSearchable == null) { 820b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn return; 821b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn } 822d21572cd446e56efe94689324107927ac2bffd2aSatish Sampath if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) { 823662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath // The user changed the query, check if it is a URL and if so change the search 824662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath // button in the soft keyboard to the 'Go' button. 825662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)); 826662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (Regex.WEB_URL_PATTERN.matcher(mUserQuery).matches()) { 827662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath options = options | EditorInfo.IME_ACTION_GO; 828662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } else { 829662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath options = options | EditorInfo.IME_ACTION_SEARCH; 830662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 831662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (options != mSearchAutoCompleteImeOptions) { 832662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoCompleteImeOptions = options; 833662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoComplete.setImeOptions(options); 834662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath // This call is required to update the soft keyboard UI with latest IME flags. 835662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType()); 836662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 837662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 838662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Enable/Disable the cancel button based on edit text state (any text?) 8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateWidgetState() { 8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // enable the button if we have one or more non-space characters 846875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen boolean enabled = !mSearchAutoComplete.isEmpty(); 8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton.setEnabled(enabled); 8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mGoButton.setFocusable(enabled); 8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * React to typing in the GO search button by refocusing to EditText. 8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Continue typing the query. 8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project View.OnKeyListener mButtonsKeyListener = new View.OnKeyListener() { 8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKey(View v, int keyCode, KeyEvent event) { 857875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // guard against possible race conditions 858875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable == null) { 859875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 860875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 861875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 862875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!event.isSystem() && 863875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen (keyCode != KeyEvent.KEYCODE_DPAD_UP) && 864875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) && 865875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) && 866875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) { 867875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // restore focus and give key to EditText ... 868875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchAutoComplete.requestFocus()) { 869875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return mSearchAutoComplete.dispatchKeyEvent(event); 870875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 872875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * React to a click in the GO button by launching a search. 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project View.OnClickListener mGoButtonClickListener = new View.OnClickListener() { 8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onClick(View v) { 882875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // guard against possible race conditions 883875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable == null) { 884875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 886875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen launchQuerySearch(); 8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * React to a click in the voice search button. 8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() { 8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onClick(View v) { 895875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // guard against possible race conditions 896875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable == null) { 897875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 898875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 900c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau // First stop the existing search before starting voice search, or else we'll end 901c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau // up showing the search dialog again once we return to the app. 902c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau ((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE)). 903c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau stopSearch(); 904c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau 9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceSearchLaunchWebSearch()) { 9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getContext().startActivity(mVoiceWebSearchIntent); 9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { 908c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent); 9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getContext().startActivity(appSearchIntent); 9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (ActivityNotFoundException e) { 9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Should not happen, since we check the availability of 9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // voice search before showing the button. But just in case... 9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(LOG_TAG, "Could not find voice search activity"); 9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Create and return an Intent that can launch the voice search activity, perform a specific 9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * voice transcription, and forward the results to the searchable activity. 9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param baseIntent The voice app search intent to start from 9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return A completely-configured intent ready to send to the voice search activity 9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Intent createVoiceAppSearchIntent(Intent baseIntent) { 927260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau ComponentName searchActivity = mSearchable.getSearchActivity(); 928260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau 9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // create the necessary intent to set up a search-and-forward operation 9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // in the voice search system. We have to keep the bundle separate, 9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // because it becomes immutable once it enters the PendingIntent 9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Intent queryIntent = new Intent(Intent.ACTION_SEARCH); 933260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau queryIntent.setComponent(searchActivity); 9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project PendingIntent pending = PendingIntent.getActivity( 9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT); 9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Now set up the bundle that will be inserted into the pending intent 9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // when it's time to do the search. We always build it here (even if empty) 9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // because the voice search activity will always need to insert "QUERY" into 9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // it anyway. 9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Bundle queryExtras = new Bundle(); 9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mAppSearchData != null) { 9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData); 9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Now build the intent to launch the voice search. Add all necessary 9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // extras to launch the voice recognizer, and then all the necessary extras 9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to forward the results to the searchable activity 9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Intent voiceIntent = new Intent(baseIntent); 9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Add all of the configuration options supplied by the searchable's metadata 9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; 9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String prompt = null; 9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String language = null; 9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int maxResults = 1; 9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Resources resources = mActivityContext.getResources(); 9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceLanguageModeId() != 0) { 9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project languageModel = resources.getString(mSearchable.getVoiceLanguageModeId()); 9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoicePromptTextId() != 0) { 9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project prompt = resources.getString(mSearchable.getVoicePromptTextId()); 9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceLanguageId() != 0) { 9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project language = resources.getString(mSearchable.getVoiceLanguageId()); 9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSearchable.getVoiceMaxResults() != 0) { 9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project maxResults = mSearchable.getVoiceMaxResults(); 9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); 9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); 9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); 9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); 973260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau voiceIntent.putExtra(EXTRA_CALLING_PACKAGE, 974260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau searchActivity == null ? null : searchActivity.toShortString()); 9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Add the values that configure forwarding the results 9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); 9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); 9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return voiceIntent; 9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 984662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath * Corrects http/https typo errors in the given url string, and if the protocol specifier was 985662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath * not present defaults to http. 986662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath * 987662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath * @param inUrl URL to check and fix 988662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath * @return fixed URL string. 989662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath */ 990662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath private String fixUrl(String inUrl) { 991662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) 992662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath return inUrl; 993662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 994662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) { 995662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { 996662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath inUrl = inUrl.replaceFirst("/", "//"); 997662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } else { 998662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath inUrl = inUrl.replaceFirst(":", "://"); 999662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 1000662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 1001662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 1002662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath if (inUrl.indexOf("://") == -1) { 1003662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath inUrl = "http://" + inUrl; 1004662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 1005662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 1006662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath return inUrl; 1007662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 1008662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 1009662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath /** 10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * React to the user typing "enter" or other hardwired keys while typing in the search box. 10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This handles these special keys while the edit box has focus. 10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean onKey(View v, int keyCode, KeyEvent event) { 1015875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // guard against possible race conditions 1016875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable == null) { 1017875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1020875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG_LOG_TIMING) dbgLogTiming("doTextKey()"); 1021875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) { 1022875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event 1023875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen + "), selection: " + mSearchAutoComplete.getListSelection()); 10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1025875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1026875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // If a suggestion is selected, handle enter, search key, and action keys 1027875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // as presses on the selected suggestion 1028875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchAutoComplete.isPopupShowing() && 1029875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.getListSelection() != ListView.INVALID_POSITION) { 1030875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return onSuggestionsKey(v, keyCode, event); 10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1032875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1033875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // If there is text in the query box, handle enter, and action keys 1034875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The search key is handled by the dialog's onKeyDown(). 1035875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!mSearchAutoComplete.isEmpty()) { 1036875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (keyCode == KeyEvent.KEYCODE_ENTER 1037875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen && event.getAction() == KeyEvent.ACTION_UP) { 1038875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen v.cancelLongPress(); 1039662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath 1040b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath // If this is a url entered by the user & we displayed the 'Go' button which 1041b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath // the user clicked, launch the url instead of using it as a search query. 1042b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath if (mSearchable.autoUrlDetect() && 1043b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath (mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION) 1044d21572cd446e56efe94689324107927ac2bffd2aSatish Sampath == EditorInfo.IME_ACTION_GO) { 1045b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString())); 1046b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath Intent intent = new Intent(Intent.ACTION_VIEW, uri); 1047b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1048b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath launchIntent(intent); 1049b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath } else { 1050b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath // Launch as a regular search. 1051b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath launchQuerySearch(); 1052662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath } 1053875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 1054875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1055875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (event.getAction() == KeyEvent.ACTION_DOWN) { 1056875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 1057a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) { 1058a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert launchQuerySearch(keyCode, actionKey.getQueryActionMsg()); 1059875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1063875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1067a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn @Override 1068ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen public void hide() { 1069444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert if (!isShowing()) return; 1070444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert 1071a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn // We made sure the IME was displayed, so also make sure it is closed 1072a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn // when we go away. 1073a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn InputMethodManager imm = (InputMethodManager)getContext() 1074a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn .getSystemService(Context.INPUT_METHOD_SERVICE); 1075a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn if (imm != null) { 1076a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn imm.hideSoftInputFromWindow( 1077a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn getWindow().getDecorView().getWindowToken(), 0); 1078a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn } 1079ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen 1080ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen super.hide(); 1081a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn } 1082ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen 10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1084875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * React to the user typing while in the suggestions list. First, check for action 1085875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * keys. If not handled, try refocusing regular characters into the EditText. 10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1087875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) { 1088875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // guard against possible race conditions (late arrival after dismiss) 1089875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mSearchable == null) { 1090875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 1091875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSuggestionsAdapter == null) { 10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (event.getAction() == KeyEvent.ACTION_DOWN) { 1096875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG_LOG_TIMING) { 1097875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen dbgLogTiming("onSuggestionsKey()"); 10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // First, check for enter or search (both of which we'll treat as a "click") 11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) { 1102875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen int position = mSearchAutoComplete.getListSelection(); 1103875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return launchSuggestion(position); 11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Next, check for left/right moves, which we use to "return" the user to the edit view 11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 1108875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // give "focus" to text editor, with cursor at the beginning if 1109875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // left key, at end if right key 1110875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // TODO: Reverse left/right for right-to-left languages, e.g. Arabic 11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 1112875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 0 : mSearchAutoComplete.length(); 1113875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setSelection(selPoint); 1114875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setListSelection(0); 1115875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.clearListSelection(); 1116ffe3ecf2b1ee04288008758c0f60ae22238797c1Mike LeBeau mSearchAutoComplete.ensureImeVisible(); 1117ffe3ecf2b1ee04288008758c0f60ae22238797c1Mike LeBeau 11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Next, check for an "up and out" move 1122875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (keyCode == KeyEvent.KEYCODE_DPAD_UP 1123875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen && 0 == mSearchAutoComplete.getListSelection()) { 1124875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen restoreUserQuery(); 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // let ACTV complete the move 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Next, check for an "action key" 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode); 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((actionKey != null) && 1132a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert ((actionKey.getSuggestActionMsg() != null) || 1133a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert (actionKey.getSuggestActionMsgColumn() != null))) { 1134875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // launch suggestion using action key column 1135875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen int position = mSearchAutoComplete.getListSelection(); 1136875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (position != ListView.INVALID_POSITION) { 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Cursor c = mSuggestionsAdapter.getCursor(); 11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c.moveToPosition(position)) { 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String actionMsg = getActionKeyMessage(c, actionKey); 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (actionMsg != null && (actionMsg.length() > 0)) { 1141875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return launchSuggestion(position, keyCode, actionMsg); 11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1148875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1149875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1150875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1151875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Launch a search for the text in the query text field. 1152875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1153d2d6014f715f12f6263f61ba3eeb6f8cba6d0fa6krosaen public void launchQuerySearch() { 1154875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null); 1155875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1158875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Launch a search for the text in the query text field. 1159875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1160875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionKey The key code of the action key that was pressed, 1161875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1162875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionMsg The message for the action key that was pressed, 1163875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or <code>null</code> if none. 1164875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1165875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen protected void launchQuerySearch(int actionKey, String actionMsg) { 1166875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String query = mSearchAutoComplete.getText().toString(); 11674899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH; 11684899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert Intent intent = createIntent(action, null, null, query, null, 1169be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert actionKey, actionMsg, null); 11702fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert // Allow GlobalSearch to log and create shortcut for searches launched by 11712fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert // the search button, enter key or an action key. 11722fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert if (mGlobalSearchMode) { 11732fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert mSuggestionsAdapter.reportSearch(query); 11742fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert } 1175875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen launchIntent(intent); 1176875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1177875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1178875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1179875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Launches an intent based on a suggestion. 11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1181875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param position The index of the suggestion to create the intent from. 1182875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return true if a successful launch, false if could not (e.g. bad position). 11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1184875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen protected boolean launchSuggestion(int position) { 1185875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); 1186875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1187875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1188875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1189875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Launches an intent based on a suggestion. 1190875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1191875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param position The index of the suggestion to create the intent from. 1192875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionKey The key code of the action key that was pressed, 1193875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1194875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionMsg The message for the action key that was pressed, 1195875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or <code>null</code> if none. 1196875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return true if a successful launch, false if could not (e.g. bad position). 1197875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1198875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { 1199875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Cursor c = mSuggestionsAdapter.getCursor(); 1200875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if ((c != null) && c.moveToPosition(position)) { 1201a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1202875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); 1203d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1204d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // report back about the click 1205d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen if (mGlobalSearchMode) { 1206d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // in global search mode, do it via cursor 12075417f09c141713c62be697fe10fe149d6d3d0eb4Bjorn Bringert mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg); 1208d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } else if (intent != null 1209d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen && mPreviousComponents != null 1210d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen && !mPreviousComponents.isEmpty()) { 1211d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // in-app search (and we have pivoted in as told by mPreviousComponents, 1212d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // which is used for keeping track of what we pop back to when we are pivoting into 1213d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // in app search.) 1214d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen reportInAppClickToGlobalSearch(c, intent); 1215d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } 1216d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1217d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // launch the intent 1218875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen launchIntent(intent); 1219a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1220875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 1221875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1222875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 1223875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1224a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1225875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1226d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * Report a click from an in app search result back to global search for shortcutting porpoises. 1227d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * 1228d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * @param c The cursor that is pointing to the clicked position. 1229d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * @param intent The intent that will be launched for the click. 1230d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen */ 1231d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { 1232d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // for in app search, still tell global search via content provider 1233d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen Uri uri = getClickReportingUri(); 1234d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen final ContentValues cv = new ContentValues(); 1235d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); 1236d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen final ComponentName source = mSearchable.getSearchActivity(); 1237d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); 1238d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1239d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // grab the intent columns from the intent we created since it has additional 1240d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // logic for falling back on the searchable default 1241d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); 1242d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); 1243bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, 12440e62e2df369b1f5a8e8ed38d3a6af6f568caf5e4Bjorn Bringert intent.getComponent().flattenToShortString()); 1245d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1246d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // ensure the icons will work for global search 1247d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, 1248d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen wrapIconForPackage( 124983dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert mSearchable.getSuggestPackage(), 1250d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); 1251d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, 1252d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen wrapIconForPackage( 125383dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert mSearchable.getSuggestPackage(), 1254d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); 1255d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1256d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // the rest can be passed through directly 1257d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, 1258d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); 1259d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, 1260d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); 1261d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, 1262d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); 1263d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_QUERY, 1264d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); 1265d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, 1266d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); 1267d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // note: deliberately omitting background color since it is only for global search 1268d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // "more results" entries 1269d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen mContext.getContentResolver().insert(uri, cv); 1270d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } 1271d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1272d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen /** 1273d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * @return A URI appropriate for reporting a click. 1274d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen */ 1275d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen private Uri getClickReportingUri() { 1276d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen Uri.Builder uriBuilder = new Uri.Builder() 1277d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .scheme(ContentResolver.SCHEME_CONTENT) 1278d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); 1279d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1280d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); 1281d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1282d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen return uriBuilder 1283d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() 1284d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() 1285d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .build(); 1286d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } 1287d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1288d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen /** 1289d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * Wraps an icon for a particular package. If the icon is a resource id, it is converted into 1290d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * an android.resource:// URI. 1291d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * 129283dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert * @param packageName The source of the icon 1293d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * @param icon The icon retrieved from a suggestion column 1294d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * @return An icon string appropriate for the package. 1295d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen */ 129683dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert private String wrapIconForPackage(String packageName, String icon) { 1297d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen if (icon == null || icon.length() == 0 || "0".equals(icon)) { 1298d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // SearchManager specifies that null or zero can be returned to indicate 1299d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen // no icon. We also allow empty string. 1300d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen return null; 1301d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } else if (!Character.isDigit(icon.charAt(0))){ 1302d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen return icon; 1303d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } else { 1304d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen return new Uri.Builder() 1305d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 1306d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .authority(packageName) 1307d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .encodedPath(icon) 1308d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen .toString(); 1309d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } 1310d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen } 1311d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen 1312d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen /** 1313ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen * Launches an intent, including any special intent handling. Doesn't dismiss the dialog 1314ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen * since that will be handled in {@link SearchDialogWrapper#performActivityResuming} 1315875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1316875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void launchIntent(Intent intent) { 1317875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (intent == null) { 13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1320875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (handleSpecialIntent(intent)){ 1321875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 1322875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1323ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen Log.d(LOG_TAG, "launching " + intent); 13244899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert try { 13254899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // in global search mode, we send the activity straight to the original suggestion 13264899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // source. this is because GlobalSearch may not have permission to launch the 13274899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // intent, and to avoid the extra step of going through GlobalSearch. 13284899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (mGlobalSearchMode) { 1329214b6b9ef91320cd7b2eab592fbe0eec1614f3cbBryan Mawhinney launchGlobalSearchIntent(intent); 133042b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney if (mStoredComponentName != null) { 133142b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney // If we're embedded in an application, dismiss the dialog. 133242b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney // This ensures that if the intent is handled by the current 133342b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney // activity, it's not obscured by the dialog. 133442b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney dismiss(); 133542b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney } 13364899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } else { 1337725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert // If the intent was created from a suggestion, it will always have an explicit 1338725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert // component here. 1339725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI()); 13404899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert getContext().startActivity(intent); 1341da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert // If the search switches to a different activity, 1342da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert // SearchDialogWrapper#performActivityResuming 13434899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // will handle hiding the dialog when the next activity starts, but for 1344da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert // real in-app search, we still need to dismiss the dialog. 1345da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert if (isInRealAppSearch()) { 1346da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert dismiss(); 1347da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert } 13484899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 13494899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } catch (RuntimeException ex) { 13504899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert Log.e(LOG_TAG, "Failed launch activity: " + intent, ex); 13514899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 13524899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 1353876627d8c52300608c8c716f980a8686f43bf476Karl Rosaen 13544899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert private void launchGlobalSearchIntent(Intent intent) { 13554899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert final String packageName; 13564899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // GlobalSearch puts the original source of the suggestion in the 13574899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // 'component name' column. If set, we send the intent to that activity. 13584899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // We trust GlobalSearch to always set this to the suggestion source. 13594899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY); 13604899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (intentComponent != null) { 13614899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert ComponentName componentName = ComponentName.unflattenFromString(intentComponent); 13624899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert intent.setComponent(componentName); 13634899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert intent.removeExtra(SearchManager.COMPONENT_NAME_KEY); 13644899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // Launch the intent as the suggestion source. 13654899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // This prevents sources from using the search dialog to launch 13664899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // intents that they don't have permission for themselves. 13674899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert packageName = componentName.getPackageName(); 13684899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } else { 13694899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // If there is no component in the suggestion, it must be a built-in suggestion 13704899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // from GlobalSearch (e.g. "Search the web for") or the intent 13714899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // launched when pressing the search/go button in the search dialog. 13724899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // Launch the intent with the permissions of GlobalSearch. 13734899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert packageName = mSearchable.getSearchActivity().getPackageName(); 1374876627d8c52300608c8c716f980a8686f43bf476Karl Rosaen } 13754899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 13764899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // Launch all global search suggestions as new tasks, since they don't relate 13774899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // to the current task. 13784899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 13794899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert setBrowserApplicationId(intent); 13804899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 13814899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert startActivityInPackage(intent, packageName); 13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13834899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 13844899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert /** 13854899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * If the intent is to open an HTTP or HTTPS URL, we set 13864899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that 13874899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * has been opened by us for the same URL will be reused. 13884899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert */ 13894899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert private void setBrowserApplicationId(Intent intent) { 13904899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert Uri data = intent.getData(); 13914899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null) { 13924899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert String scheme = data.getScheme(); 13934899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (scheme != null && scheme.startsWith("http")) { 13944899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert intent.putExtra(Browser.EXTRA_APPLICATION_ID, data.toString()); 13954899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 13964899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 13974899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 13984899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 13994899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert /** 14004899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * Starts an activity as if it had been started by the given package. 14014899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * 14024899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * @param intent The description of the activity to start. 14034899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * @param packageName 14044899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * @throws ActivityNotFoundException If the intent could not be resolved to 14054899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * and existing activity. 14064899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * @throws SecurityException If the package does not have permission to start 14074899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * start the activity. 14084899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert * @throws AndroidRuntimeException If some other error occurs. 14094899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert */ 14104899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert private void startActivityInPackage(Intent intent, String packageName) { 14114899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert try { 14124899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert int uid = ActivityThread.getPackageManager().getPackageUid(packageName); 14134899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (uid < 0) { 14144899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new AndroidRuntimeException("Package UID not found " + packageName); 14154899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14164899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver()); 14174899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert IBinder resultTo = null; 14184899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert String resultWho = null; 14194899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert int requestCode = -1; 14204899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert boolean onlyIfNeeded = false; 1421725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert Log.i(LOG_TAG, "Starting (uid " + uid + ", " + packageName + ") " + intent.toURI()); 14224899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert int result = ActivityManagerNative.getDefault().startActivityInPackage( 14234899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded); 14244899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert checkStartActivityResult(result, intent); 14254899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } catch (RemoteException ex) { 14264899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new AndroidRuntimeException(ex); 14274899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14284899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14294899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 14304899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // Stolen from Instrumentation.checkStartActivityResult() 14314899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert private static void checkStartActivityResult(int res, Intent intent) { 14324899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (res >= IActivityManager.START_SUCCESS) { 14334899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert return; 14344899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14354899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert switch (res) { 14364899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert case IActivityManager.START_INTENT_NOT_RESOLVED: 14374899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert case IActivityManager.START_CLASS_NOT_FOUND: 14384899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (intent.getComponent() != null) 14394899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new ActivityNotFoundException( 14404899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert "Unable to find explicit activity class " 14414899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert + intent.getComponent().toShortString() 14424899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert + "; have you declared this activity in your AndroidManifest.xml?"); 14434899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new ActivityNotFoundException( 14444899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert "No Activity found to handle " + intent); 14454899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert case IActivityManager.START_PERMISSION_DENIED: 14464899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new SecurityException("Not allowed to start activity " 14474899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert + intent); 14484899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 14494899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new AndroidRuntimeException( 14504899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert "FORWARD_RESULT_FLAG used while also requesting a result"); 14514899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert default: 14524899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert throw new AndroidRuntimeException("Unknown error code " 14534899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert + res + " when starting " + intent); 14544899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14554899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 14564899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert 14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1458875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Handles the special intent actions declared in {@link SearchManager}. 1459875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1460875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return <code>true</code> if the intent was handled. 14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1462875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private boolean handleSpecialIntent(Intent intent) { 1463875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String action = intent.getAction(); 1464875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) { 1465875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen handleChangeSourceIntent(intent); 1466875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1468875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 1469875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1470875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1471875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1472d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. 1473875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1474875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void handleChangeSourceIntent(Intent intent) { 1475875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Uri dataUri = intent.getData(); 1476875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (dataUri == null) { 1477875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.w(LOG_TAG, "SearchManager.INTENT_ACTION_CHANGE_SOURCE without intent data."); 1478875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1480875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen ComponentName componentName = ComponentName.unflattenFromString(dataUri.toString()); 1481875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (componentName == null) { 1482875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.w(LOG_TAG, "Invalid ComponentName: " + dataUri); 1483875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1485875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Switching to " + componentName); 1486da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert 1487da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert pushPreviousComponent(mLaunchComponent); 1488875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (!show(componentName, mAppSearchData, false)) { 1489875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.w(LOG_TAG, "Failed to switch to source " + componentName); 1490da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert popPreviousComponent(); 1491875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1494875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String query = intent.getStringExtra(SearchManager.QUERY); 1495875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen setUserQuery(query); 149635df87c6319863beed9fd95d0a279e34ff94e92eMike LeBeau mSearchAutoComplete.showDropDown(); 14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1498a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1499875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1500ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau * Sets the list item selection in the AutoCompleteTextView's ListView. 1501ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau */ 1502ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau public void setListSelection(int index) { 1503ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau mSearchAutoComplete.setListSelection(index); 1504ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau } 1505a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1506ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau /** 150787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert * Checks if there are any previous searchable components in the history stack. 150887d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert */ 150987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert private boolean hasPreviousComponent() { 151087d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert return mPreviousComponents != null && !mPreviousComponents.isEmpty(); 151187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert } 151287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 151387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert /** 1514875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Saves the previous component that was searched, so that we can go 1515875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * back to it. 1516875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1517875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void pushPreviousComponent(ComponentName componentName) { 1518875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mPreviousComponents == null) { 1519875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mPreviousComponents = new ArrayList<ComponentName>(); 1520875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1521875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mPreviousComponents.add(componentName); 1522875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1523875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1524875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1525875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Pops the previous component off the stack and returns it. 1526875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1527875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return The component name, or <code>null</code> if there was 1528875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * no previous component. 1529875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1530875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private ComponentName popPreviousComponent() { 153187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert if (!hasPreviousComponent()) { 1532875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return null; 1533875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 153487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert return mPreviousComponents.remove(mPreviousComponents.size() - 1); 1535875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1536875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1537875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1538875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Goes back to the previous component that was searched, if any. 1539875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1540875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return <code>true</code> if there was a previous component that we could go back to. 1541875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 154287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert private boolean backToPreviousComponent() { 1543875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen ComponentName previous = popPreviousComponent(); 1544875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (previous == null) { 1545875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return false; 1546875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 154787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 154887d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert if (!show(previous, mAppSearchData, false)) { 154987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert Log.w(LOG_TAG, "Failed to switch to source " + previous); 155087d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert return false; 155183fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn } 155287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 155387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert // must touch text to trigger suggestions 155487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert // TODO: should this be the text as it was when the user left 155587d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert // the source that we are now going back to? 155687d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert String query = mSearchAutoComplete.getText().toString(); 155787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert setUserQuery(query); 1558875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return true; 15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * When a particular suggestion has been selected, perform the various lookups required 15639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to use the suggestion. This includes checking the cursor for suggestion-specific data, 15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and/or falling back to the XML for defaults; It also creates REST style Uri data when 15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the suggestion includes a data id. 15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 15679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param c The suggestions cursor, moved to the row of the user's selection 1568875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionKey The key code of the action key that was pressed, 1569875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1570875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionMsg The message for the action key that was pressed, 1571875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or <code>null</code> if none. 1572875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return An intent for the suggestion at the cursor's position. 15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1574875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) { 15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // use specific action if supplied, or default action if supplied, or fixed default 1577875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); 1578a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1579a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen // some items are display only, or have effect via the cursor respond click reporting. 1580a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen if (SearchManager.INTENT_ACTION_NONE.equals(action)) { 1581a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen return null; 1582a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen } 1583a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1584875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (action == null) { 1585875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen action = mSearchable.getSuggestIntentAction(); 15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1587875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (action == null) { 1588875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen action = Intent.ACTION_SEARCH; 15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // use specific data if supplied, or default data if supplied 1592875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA); 15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (data == null) { 1594875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen data = mSearchable.getSuggestIntentData(); 15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // then, if an ID was provided, append it. 15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (data != null) { 1598875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); 1599875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (id != null) { 1600875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen data = data + "/" + Uri.encode(id); 16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1603875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Uri dataUri = (data == null) ? null : Uri.parse(data); 1604875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1605bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath String componentName = getColumnString( 1606bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME); 1607a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen 1608875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); 1609bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); 1610be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null; 1611875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1612bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath return createIntent(action, dataUri, extraData, query, componentName, actionKey, 1613be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert actionMsg, mode); 16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (RuntimeException e ) { 16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int rowNum; 16169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { // be really paranoid now 16179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rowNum = c.getPosition(); 16189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (RuntimeException e2 ) { 16199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rowNum = -1; 16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum + 16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project " returned exception" + e.toString()); 1623875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return null; 16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1628875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Constructs an intent from the given information and the search dialog state. 1629875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * 1630875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param action Intent action. 1631875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param data Intent data, or <code>null</code>. 1632875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. 1633bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath * @param query Intent query, or <code>null</code>. 1634bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>. 1635875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionKey The key code of the action key that was pressed, 1636875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. 1637875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @param actionMsg The message for the action key that was pressed, 1638875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * or <code>null</code> if none. 1639be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert * @param mode The search mode, one of the acceptable values for 1640be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert * {@link SearchManager#SEARCH_MODE}, or {@code null}. 1641875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * @return The intent. 1642875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1643bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath private Intent createIntent(String action, Uri data, String extraData, String query, 1644be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert String componentName, int actionKey, String actionMsg, String mode) { 1645875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Now build the Intent 1646875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Intent intent = new Intent(action); 16478d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1648442da48b24f75e6075763b89943b513197a0bfe9Bjorn Bringert // We need CLEAR_TOP to avoid reusing an old task that has other activities 1649dc1396c881b80074c99d171aee02d3a5486180caEvan Millar // on top of the one we want. We don't want to do this in in-app search though, 1650dc1396c881b80074c99d171aee02d3a5486180caEvan Millar // as it can be destructive to the activity stack. 1651dc1396c881b80074c99d171aee02d3a5486180caEvan Millar if (mGlobalSearchMode) { 1652dc1396c881b80074c99d171aee02d3a5486180caEvan Millar intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 1653dc1396c881b80074c99d171aee02d3a5486180caEvan Millar } 1654875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (data != null) { 1655875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.setData(data); 1656875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 16575f80605a0b866f34c74955e190909bd6ab5d992eBjorn Bringert intent.putExtra(SearchManager.USER_QUERY, mUserQuery); 1658875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (query != null) { 1659875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.putExtra(SearchManager.QUERY, query); 1660875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1661875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (extraData != null) { 1662875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); 1663875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1664bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath if (componentName != null) { 1665bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName); 1666bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath } 1667875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (mAppSearchData != null) { 1668875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.putExtra(SearchManager.APP_DATA, mAppSearchData); 1669875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1670875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { 1671875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.putExtra(SearchManager.ACTION_KEY, actionKey); 1672875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen intent.putExtra(SearchManager.ACTION_MSG, actionMsg); 1673875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1674be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert if (mode != null) { 1675be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert intent.putExtra(SearchManager.SEARCH_MODE, mode); 1676be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert } 16774899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert // Only allow 3rd-party intents from GlobalSearch 16784899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert if (!mGlobalSearchMode) { 16794899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert intent.setComponent(mSearchable.getSearchActivity()); 16804899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert } 1681875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return intent; 1682875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1683875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1684875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 16859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For a given suggestion and a given cursor row, get the action message. If not provided 16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * by the specific row/column, also check for a single definition (for the action key). 16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param c The cursor providing suggestions 16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param actionKey The actionkey record being examined 16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return Returns a string, or null if no action key message for this suggestion 16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1693875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) { 16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String result = null; 16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // check first in the cursor data, for a suggestion-specific message 1696a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert final String column = actionKey.getSuggestActionMsgColumn(); 16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (column != null) { 1698875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen result = SuggestionsAdapter.getColumnString(c, column); 16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If the cursor didn't give us a message, see if there's a single message defined 17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // for the actionkey (for all suggestions) 17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result == null) { 1703a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert result = actionKey.getSuggestActionMsg(); 17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return result; 17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1707077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1708077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert /** 1709077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert * The root element in the search bar layout. This is a custom view just to override 1710077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert * the handling of the back button. 1711077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert */ 1712077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert public static class SearchBar extends LinearLayout { 1713077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1714077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert private SearchDialog mSearchDialog; 1715077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1716077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert public SearchBar(Context context, AttributeSet attrs) { 1717077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert super(context, attrs); 1718077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1719077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1720077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert public SearchBar(Context context) { 1721077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert super(context); 1722077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1723077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1724077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert public void setSearchDialog(SearchDialog searchDialog) { 1725077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert mSearchDialog = searchDialog; 1726077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1727077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 1728077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert /** 1729077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert * Overrides the handling of the back key to move back to the previous sources or dismiss 1730077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert * the search dialog, instead of dismissing the input method. 1731077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert */ 1732077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert @Override 1733077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert public boolean dispatchKeyEventPreIme(KeyEvent event) { 1734077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + event + ")"); 1735077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 17365b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert KeyEvent.DispatcherState state = getKeyDispatcherState(); 17375b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert if (state != null) { 17385b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert if (event.getAction() == KeyEvent.ACTION_DOWN 17395b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert && event.getRepeatCount() == 0) { 17405b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert state.startTracking(event, this); 17415b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert return true; 17425b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert } else if (event.getAction() == KeyEvent.ACTION_UP 17435b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert && !event.isCanceled() && state.isTracking(event)) { 17445b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert mSearchDialog.onBackPressed(); 17455b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert return true; 17465b6b41241d97ca6c08b610a421b758815a451803Bjorn Bringert } 1747077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1748077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1749077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert return super.dispatchKeyEventPreIme(event); 1750077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1751077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert } 1752077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1754875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Local subclass for AutoCompleteTextView. 17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class SearchAutoComplete extends AutoCompleteTextView { 17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1758875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private int mThreshold; 1759077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert 17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SearchAutoComplete(Context context) { 17611746d6fefcb1b4869f475a81f5aec35db4695603Marco Nelissen super(context); 1762875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mThreshold = getThreshold(); 17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SearchAutoComplete(Context context, AttributeSet attrs) { 17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(context, attrs); 1767875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mThreshold = getThreshold(); 17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) { 17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(context, attrs, defStyle); 1772875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mThreshold = getThreshold(); 17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1774875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 1776875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen public void setThreshold(int threshold) { 1777875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen super.setThreshold(threshold); 1778875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mThreshold = threshold; 17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1782875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Returns true if the text field is empty, or contains only whitespace. 17839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1784875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private boolean isEmpty() { 1785875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return TextUtils.getTrimmedLength(getText()) == 0; 17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17878d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert 17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1789875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * We override this method to avoid replacing the query box text 1790875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * when a suggestion is clicked. 17919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 1793875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen protected void replaceText(CharSequence text) { 17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1797617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau * We override this method to avoid an extra onItemClick being called on the 1798617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} 1799617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau * when an item is clicked with the trackball. 1800617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau */ 1801617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau @Override 1802617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau public void performCompletion() { 1803617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau } 18041fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau 18051fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau /** 18061fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau * We override this method to be sure and show the soft keyboard if appropriate when 18071fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau * the TextView has focus. 18081fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau */ 18091fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau @Override 18101fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau public void onWindowFocusChanged(boolean hasWindowFocus) { 18111fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau super.onWindowFocusChanged(hasWindowFocus); 18121fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau 18131fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau if (hasWindowFocus) { 18141fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau InputMethodManager inputManager = (InputMethodManager) 18151fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 18161fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau inputManager.showSoftInput(this, 0); 18171fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau } 18181fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau } 18191fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau 1820617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau /** 1821875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * We override this method so that we can allow a threshold of zero, which ACTV does not. 18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 1824875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen public boolean enoughToFilter() { 1825875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return mThreshold <= 0 || super.enoughToFilter(); 18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 182798e333f551a4bf2ebb50bb97a2a56b14bfdcd74bKarl Rosaen 1828875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 182987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 183087d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert @Override 183187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert public void onBackPressed() { 1832a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert // If the input method is covering the search dialog completely, 1833a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert // e.g. in landscape mode with no hard keyboard, dismiss just the input method 1834a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert InputMethodManager imm = (InputMethodManager)getContext() 1835a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert .getSystemService(Context.INPUT_METHOD_SERVICE); 1836a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert if (imm != null && imm.isFullscreenMode() && 1837a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) { 1838a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert return; 1839a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert } 1840a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert // Otherwise, go back to any previous source (e.g. back to QSB when 1841a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert // pivoted into a source. 184287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert if (!backToPreviousComponent()) { 1843a6309c30722ad3aee88ddc2394cea2a499168d99Bjorn Bringert // If no previous source, close search dialog 184487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert cancel(); 18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 184787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert 18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Implements OnItemClickListener 18509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1852875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position); 1853875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen launchSuggestion(position); 18549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1855875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Implements OnItemSelectedListener 18589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 1860875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position); 1861875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // A suggestion has been selected, rewrite the query if possible, 1862875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // otherwise the restore the original query. 1863875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (REWRITE_QUERIES) { 1864875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen rewriteQueryFromSuggestion(position); 1865875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 18669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 18679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 18699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Implements OnItemSelectedListener 18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onNothingSelected(AdapterView<?> parent) { 1872875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "onNothingSelected()"); 1873875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1874875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1875875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1876875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Query rewriting. 1877875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1878875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1879875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void rewriteQueryFromSuggestion(int position) { 1880875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Cursor c = mSuggestionsAdapter.getCursor(); 1881875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (c == null) { 1882875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen return; 1883875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1884875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (c.moveToPosition(position)) { 1885875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Get the new query from the suggestion. 1886875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen CharSequence newQuery = mSuggestionsAdapter.convertToString(c); 1887875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (newQuery != null) { 1888875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The suggestion rewrites the query. 1889875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Rewriting query to '" + newQuery + "'"); 1890875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // Update the text field, without getting new suggestions. 1891875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen setQuery(newQuery); 1892875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } else { 1893875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // The suggestion does not rewrite the query, restore the user's query. 1894875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Suggestion gives no rewrite, restoring user query."); 1895875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen restoreUserQuery(); 1896875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1897875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } else { 1898875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen // We got a bad position, restore the user's query. 1899875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen Log.w(LOG_TAG, "Bad suggestion position: " + position); 1900875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen restoreUserQuery(); 1901875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1902875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1903875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1904875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1905875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Restores the query entered by the user if needed. 1906875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1907875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void restoreUserQuery() { 1908875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (DBG) Log.d(LOG_TAG, "Restoring query to '" + mUserQuery + "'"); 1909875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen setQuery(mUserQuery); 1910875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1911875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1912875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1913875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Sets the text in the query box, without updating the suggestions. 1914875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1915875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void setQuery(CharSequence query) { 1916875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setText(query, false); 1917875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (query != null) { 1918875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setSelection(query.length()); 1919875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1920875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1921875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen 1922875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen /** 1923875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Sets the text in the query box, updating the suggestions. 1924875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */ 1925875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen private void setUserQuery(String query) { 1926875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen if (query == null) { 1927875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen query = ""; 1928875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen } 1929875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mUserQuery = query; 1930875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setText(query); 1931875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen mSearchAutoComplete.setSelection(query.length()); 19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 19359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Debugging Support 19369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 19379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 19399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For debugging only, sample the millisecond clock and log it. 19409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Uses AtomicLong so we can use in multiple threads 19419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 19429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private AtomicLong mLastLogTime = new AtomicLong(SystemClock.uptimeMillis()); 19439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void dbgLogTiming(final String caller) { 19449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long millis = SystemClock.uptimeMillis(); 19459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long oldTime = mLastLogTime.getAndSet(millis); 19469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long delta = millis - oldTime; 19479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller; 19489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.d(LOG_TAG,report); 19499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 19509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1951