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