SearchDialog.java revision 29d52e120f36b46d7ef596b1c182db52d3476be7
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";
99b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert
1001fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau    private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
1011fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau    private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
102444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // views & widgets
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextView mBadgeLabel;
1051fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau    private ImageView mAppIcon;
106875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private SearchAutoComplete mSearchAutoComplete;
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Button mGoButton;
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ImageButton mVoiceButton;
109875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private View mSearchPlate;
110f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy    private Drawable mWorkingSpinner;
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // interaction with searchable application
113875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private SearchableInfo mSearchable;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ComponentName mLaunchComponent;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Bundle mAppSearchData;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mGlobalSearchMode;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Context mActivityContext;
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
119b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    // Values we store to allow user to toggle between in-app search and global search.
120b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    private ComponentName mStoredComponentName;
121b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    private Bundle mStoredAppSearchData;
122b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau
123875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // stack of previous searchables, to support the BACK key after
124875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.
125875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // The top of the stack (= previous searchable) is the last element of the list,
126875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // since adding and removing is efficient at the end of an ArrayList.
127875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private ArrayList<ComponentName> mPreviousComponents;
128875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // For voice searching
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Intent mVoiceWebSearchIntent;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Intent mVoiceAppSearchIntent;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // support for AutoCompleteTextView suggestions display
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SuggestionsAdapter mSuggestionsAdapter;
135875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
136875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // Whether to rewrite queries when selecting suggestions
137cce7dbc31ed100b39c3e1174dd3d9a01de648e5bMike LeBeau    private static final boolean REWRITE_QUERIES = true;
138875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
139875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // The query entered by the user. This is not changed when selecting a suggestion
140875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // that modifies the contents of the text field. But if the user then edits
141875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // the suggestion, the resulting string is saved.
142875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private String mUserQuery;
143875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
144875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // A weak map of drawables we've gotten from other packages, so we don't load them
145875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // more than once.
146dfefa3e915938aa1eeeaa7c0910a882bded7bcf7Bjorn Bringert    private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
147dfefa3e915938aa1eeeaa7c0910a882bded7bcf7Bjorn Bringert            new WeakHashMap<String, Drawable.ConstantState>();
148662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
149662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath    // Last known IME options value for the search edit text.
150662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath    private int mSearchAutoCompleteImeOptions;
151662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Constructor - fires it up and makes it look like the search UI.
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context Application Context we can use for system acess
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public SearchDialog(Context context) {
158a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau        super(context, com.android.internal.R.style.Theme_GlobalSearchBar);
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * We create the search dialog just once, and it stays around (hidden)
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * until activated by the user.
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onCreate(Bundle savedInstanceState) {
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onCreate(savedInstanceState);
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setContentView(com.android.internal.R.layout.search_bar);
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1718d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        Window theWindow = getWindow();
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        WindowManager.LayoutParams lp = theWindow.getAttributes();
1738d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR;
1748d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        lp.width = ViewGroup.LayoutParams.FILL_PARENT;
1758d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        // taking up the whole window (even when transparent) is less than ideal,
1768d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        // but necessary to show the popup window until the window manager supports
1778d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        // having windows anchored by their parent but not clipped by them.
1788d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        lp.height = ViewGroup.LayoutParams.FILL_PARENT;
1798d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
18098acd54fa6657e8131b3a2a957de7882a2f511abMike LeBeau        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        theWindow.setAttributes(lp);
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // get the view elements for local access
184077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar);
185077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        searchBar.setSearchDialog(this);
186077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
188875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete = (SearchAutoComplete)
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                findViewById(com.android.internal.R.id.search_src_text);
1901fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau        mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
193875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
194f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy        mWorkingSpinner = getContext().getResources().
1951480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau                getDrawable(com.android.internal.R.drawable.search_spinner);
196dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney        mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
197dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney                null, null, mWorkingSpinner, null);
198dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney        setWorking(false);
199dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // attach listeners
201875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.addTextChangedListener(mTextWatcher);
202875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.setOnKeyListener(mTextKeyListener);
203875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.setOnItemClickListener(this);
204875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.setOnItemSelectedListener(this);
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton.setOnClickListener(mGoButtonClickListener);
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton.setOnKeyListener(mButtonsKeyListener);
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceButton.setOnKeyListener(mButtonsKeyListener);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // pre-hide all the extraneous elements
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBadgeLabel.setVisibility(View.GONE);
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Additional adjustments to make Dialog work for Search
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Touching outside of the search dialog will dismiss it
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setCanceledOnTouchOutside(true);
217444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Save voice intent for later queries/launching
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
2208d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
2258d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
226662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
227662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions();
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set up the search dialog
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
233875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return true if search dialog launched, false if not
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean show(String initialQuery, boolean selectInitialQuery,
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ComponentName componentName, Bundle appSearchData, boolean globalSearch) {
2378d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert
238b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        // Reset any stored values from last time dialog was shown.
239b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        mStoredComponentName = null;
240b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        mStoredAppSearchData = null;
241fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath
242fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath        boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
243fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath                globalSearch);
244fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath        if (success) {
245fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath            // Display the drop down as soon as possible instead of waiting for the rest of the
246fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath            // pending UI stuff to get done, so that things appear faster to the user.
247fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath            mSearchAutoComplete.showDropDownAfterLayout();
248fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath        }
249fef8d3e4d8f6f46c098d04b3a57409c947ec1c75Satish Sampath        return success;
250b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    }
251da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert
252da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert    private boolean isInRealAppSearch() {
253da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert        return !mGlobalSearchMode
254da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                && (mPreviousComponents == null || mPreviousComponents.isEmpty());
255da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert    }
256da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert
257b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    /**
258b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * Called in response to a press of the hard search button in
259b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app
260b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * search and global search when relevant.
261b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     *
262b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * If pressed within an in-app search context, this switches the search dialog out to
263b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * global search. If pressed within a global search context that was originally an in-app
264b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * search context, this switches back to the in-app search context. If pressed within a
265b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * global search context that has no original in-app search context (e.g., global search
266b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * from Home), this does nothing.
267b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     *
268b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * @return false if we wanted to toggle context but could not do so successfully, true
269b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * in all other cases
270b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     */
271b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    private boolean toggleGlobalSearch() {
272b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        String currentSearchText = mSearchAutoComplete.getText().toString();
273b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        if (!mGlobalSearchMode) {
274b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            mStoredComponentName = mLaunchComponent;
275b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            mStoredAppSearchData = mAppSearchData;
2765a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau
2775a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // If this is the browser, we have a special case to not show the icon to the left
2785a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // of the text field, for extra space for url entry (this should be reconciled in
2795a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // Eclair). So special case a second tap of the search button to remove any
2805a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // already-entered text so that we can be sure to show the "Quick Search Box" hint
2815a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // text to still make it clear to the user that we've jumped out to global search.
2825a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            //
2835a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            // TODO: When the browser icon issue is reconciled in Eclair, remove this special case.
2845a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau            if (isBrowserSearch()) currentSearchText = "";
2855a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau
286b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            return doShow(currentSearchText, false, null, mAppSearchData, true);
287b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        } else {
288b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            if (mStoredComponentName != null) {
289b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau                // This means we should toggle *back* to an in-app search context from
290b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau                // global search.
291b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau                return doShow(currentSearchText, false, mStoredComponentName,
292b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau                        mStoredAppSearchData, false);
293b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            } else {
294b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau                return true;
295b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            }
296b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau        }
297b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    }
298b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau
299b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    /**
300b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * Does the rest of the work required to show the search dialog. Called by both
301b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * {@link #show(String, boolean, ComponentName, Bundle, boolean)} and
302b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * {@link #toggleGlobalSearch()}.
303b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     *
304b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     * @return true if search dialog showed, false if not
305b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau     */
306b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau    private boolean doShow(String initialQuery, boolean selectInitialQuery,
307b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            ComponentName componentName, Bundle appSearchData,
308b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau            boolean globalSearch) {
309875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // set up the searchable and show the dialog
310875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (!show(componentName, appSearchData, globalSearch)) {
311875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return false;
312875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
313b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau
314875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // finally, load the user's initial text (which may trigger suggestions)
315875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        setUserQuery(initialQuery);
316875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (selectInitialQuery) {
317875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.selectAll();
318875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
319b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau
320875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return true;
321875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
322b3aab6958120ad8bc8d70c493ebf49dcb4fbc30eMike LeBeau
323875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
324875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Sets up the search dialog and shows it.
325875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
326875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return <code>true</code> if search dialog launched
327875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
328875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private boolean show(ComponentName componentName, Bundle appSearchData,
329875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            boolean globalSearch) {
330875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
331875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (DBG) {
332875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Log.d(LOG_TAG, "show(" + componentName + ", "
333875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    + appSearchData + ", " + globalSearch + ")");
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
335875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
3368d1538237847baf381787b881141f8c0478bef5bBjorn Bringert        SearchManager searchManager = (SearchManager)
3378d1538237847baf381787b881141f8c0478bef5bBjorn Bringert                mContext.getSystemService(Context.SEARCH_SERVICE);
338a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau        // Try to get the searchable info for the provided component (or for global search,
339a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau        // if globalSearch == true).
3408d1538237847baf381787b881141f8c0478bef5bBjorn Bringert        mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
341a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau
342a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau        // If we got back nothing, and it wasn't a request for global search, then try again
343a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau        // for global search, as we'll try to launch that in lieu of any component-specific search.
344a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau        if (!globalSearch && mSearchable == null) {
345a59d7b0f2f9af672d48041b29e147fdce1218c08Mike LeBeau            globalSearch = true;
3468d1538237847baf381787b881141f8c0478bef5bBjorn Bringert            mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
348ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert
349ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert        // If there's not even a searchable info available for global search, then really give up.
350ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert        if (mSearchable == null) {
351ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert            Log.w(LOG_TAG, "No global search provider.");
352ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert            return false;
353ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert        }
354ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLaunchComponent = componentName;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAppSearchData = appSearchData;
357875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Using globalSearch here is just an optimization, just calling
358875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // isDefaultSearchable() should always give the same result.
3598d1538237847baf381787b881141f8c0478bef5bBjorn Bringert        mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable);
360875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mActivityContext = mSearchable.getActivityContext(getContext());
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
362875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // show the dialog. this will call onStart().
3631fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau        if (!isShowing()) {
364a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            // The Dialog uses a ContextThemeWrapper for the context; use this to change the
365a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            // theme out from underneath us, between the global search theme and the in-app
366a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            // search theme. They are identical except that the global search theme does not
367a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            // dim the background of the window (because global search is full screen so it's
368a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            // not needed and this should save a little bit of time on global search invocation).
369a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            Object context = getContext();
370a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            if (context instanceof ContextThemeWrapper) {
371a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                ContextThemeWrapper wrapper = (ContextThemeWrapper) context;
372a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                if (globalSearch) {
373a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                    wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar);
374a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                } else {
375a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                    wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar);
376a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau                }
377a97f4a117415fd877bc63d6c90a6775ca04eb3feMike LeBeau            }
378875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            show();
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
380875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        updateUI();
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The search dialog is being dismissed, so handle all of the local shutdown operations.
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This function is designed to be idempotent so that dismiss() can be safely called at any time
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (even if already closed) and more likely to really dump any memory.  No leaks!
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onStop() {
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onStop();
394444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
395875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        closeSuggestionsAdapter();
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // dump extra memory we're hanging on to
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLaunchComponent = null;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAppSearchData = null;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSearchable = null;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mActivityContext = null;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mUserQuery = null;
403875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mPreviousComponents = null;
404875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
4051c6907515134c8a2e086d2eed4427f909d301935Mike LeBeau
4061c6907515134c8a2e086d2eed4427f909d301935Mike LeBeau    /**
4071480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau     * Sets the search dialog to the 'working' state, which shows a working spinner in the
4081480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau     * right hand size of the text field.
4091480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau     *
4101480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau     * @param working true to show spinner, false to hide spinner
4111480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau     */
4121480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau    public void setWorking(boolean working) {
413dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney        mWorkingSpinner.setAlpha(working ? 255 : 0);
414dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney        mWorkingSpinner.setVisible(working, false);
415dfcbc046d7fcd74c8aacd9f26bd48084900d3b4aBryan Mawhinney        mWorkingSpinner.invalidateSelf();
4161480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau    }
4171480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau
4181480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau    /**
419875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Closes and gets rid of the suggestions adapter.
420875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
421875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void closeSuggestionsAdapter() {
422875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // remove the adapter from the autocomplete first, to avoid any updates
423875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // when we drop the cursor
424875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.setAdapter((SuggestionsAdapter)null);
425875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // close any leftover cursor
426875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (mSuggestionsAdapter != null) {
427ba223760895e62ad2fcb85476984cb29018860c9Bjorn Bringert            mSuggestionsAdapter.close();
428875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
429875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSuggestionsAdapter = null;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Save the minimal set of data necessary to recreate the search
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
435444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert     * @return A bundle with the state of the dialog, or {@code null} if the search
436444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert     *         dialog is not showing.
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Bundle onSaveInstanceState() {
440444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert        if (!isShowing()) return null;
441444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Bundle bundle = new Bundle();
443444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // setup info so I can recreate this particular search
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode);
448b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName);
449b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData);
450b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents);
451b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
452b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return bundle;
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Restore the state of the dialog from a previously saved bundle.
458875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
459875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: go through this and make sure that it saves everything that is saved
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param savedInstanceState The state of the dialog previously saved by
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     {@link #onSaveInstanceState()}.
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onRestoreInstanceState(Bundle savedInstanceState) {
466444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert        if (savedInstanceState == null) return;
467444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
471b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        ComponentName storedComponentName =
472b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert                savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT);
473b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        Bundle storedAppSearchData =
474b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert                savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA);
475b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        ArrayList<ComponentName> previousComponents =
476b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert                savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS);
477b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
478b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert
479b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        // Set stored state
480b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        mStoredComponentName = storedComponentName;
481b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        mStoredAppSearchData = storedAppSearchData;
482b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        mPreviousComponents = previousComponents;
483b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert
484b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        // show the dialog.
485b0ae27f4e10a9e5fe56b9ca549718051fe342bbaBjorn Bringert        if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) {
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // for some reason, we couldn't re-instantiate
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
492875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Called after resources have changed, e.g. after screen rotation or locale change.
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
494444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert    public void onConfigurationChanged() {
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isShowing()) {
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Redraw (resources may have changed)
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateSearchButton();
4981fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            updateSearchAppIcon();
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateSearchBadge();
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateQueryHint();
50129d52e120f36b46d7ef596b1c182db52d3476be7Bryan Mawhinney            mSearchAutoComplete.showDropDownAfterLayout();
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
504875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
506875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Update the UI according to the info in the current value of {@link #mSearchable}.
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
508875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void updateUI() {
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable != null) {
510ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen            mDecor.setVisibility(View.VISIBLE);
511875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            updateSearchAutoComplete();
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateSearchButton();
5131fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            updateSearchAppIcon();
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateSearchBadge();
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateQueryHint();
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateVoiceButton();
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // In order to properly configure the input method (if one is being used), we
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // need to let it know if we'll be providing suggestions.  Although it would be
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // difficult/expensive to know if every last detail has been configured properly, we
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // can at least see if a suggestions provider has been configured, and use that
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // as our trigger.
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int inputType = mSearchable.getInputType();
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We only touch this if the input type is set up for text (which it almost certainly
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // should be, in the case of search!)
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // The existence of a suggestions authority is the proxy for "suggestions
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // are available here"
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mSearchable.getSuggestAuthority() != null) {
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
534875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.setInputType(inputType);
535662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            mSearchAutoCompleteImeOptions = mSearchable.getImeOptions();
536662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions);
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
539875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
541875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Updates the auto-complete text view.
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
543875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void updateSearchAutoComplete() {
544875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // close any existing suggestions adapter
545875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        closeSuggestionsAdapter();
546875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
547875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation
548203464af9638e4d5aecd811452af67fff9aa3087Bjorn Bringert        mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold());
5498d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        // we dismiss the entire dialog instead
5508d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
551875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
552da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert        if (!isInRealAppSearch()) {
553875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.setDropDownAlwaysVisible(true);  // fill space until results come in
554875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        } else {
555875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.setDropDownAlwaysVisible(false);
556875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
557875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
558d4760d77e7e8ab66af0307dcae39e0a8e9d90a14Mike LeBeau        mSearchAutoComplete.setForceIgnoreOutsideTouch(true);
559d4760d77e7e8ab66af0307dcae39e0a8e9d90a14Mike LeBeau
560875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // attach the suggestions adapter, if suggestions are available
561875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // The existence of a suggestions authority is the proxy for "suggestions available here"
562875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (mSearchable.getSuggestAuthority() != null) {
5631480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau            mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable,
5641480eb27f5321ef5fc2faeee87c7464e279a4912Mike LeBeau                    mOutsideDrawablesCache, mGlobalSearchMode);
565875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.setAdapter(mSuggestionsAdapter);
566875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
568875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Update the text in the search button.  Note: This is deprecated functionality, for
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * 1.0 compatibility only.
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateSearchButton() {
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String textLabel = null;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Drawable iconLabel = null;
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int textId = mSearchable.getSearchButtonText();
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (textId != 0) {
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            textLabel = mActivityContext.getResources().getString(textId);
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            iconLabel = getContext().getResources().
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    getDrawable(com.android.internal.R.drawable.ic_btn_search);
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
583875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mGoButton.setText(textLabel);
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton.setCompoundDrawablesWithIntrinsicBounds(iconLabel, null, null, null);
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5871fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau    private void updateSearchAppIcon() {
5885a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        // In Donut, we special-case the case of the browser to hide the app icon as if it were
5895a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        // global search, for extra space for url entry.
5905a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        //
5915a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        // TODO: Remove this special case once the issue has been reconciled in Eclair.
5925a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        if (mGlobalSearchMode || isBrowserSearch()) {
5931fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mAppIcon.setImageResource(0);
5941fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mAppIcon.setVisibility(View.GONE);
5951fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
5961fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingTop(),
5971fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingRight(),
5981fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingBottom());
5991fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau        } else {
6001fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            PackageManager pm = getContext().getPackageManager();
601f4f7046183f0daedd17703fa16cf9026442bdaeeRomain Guy            Drawable icon;
6021fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            try {
6031fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
6041fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                icon = pm.getApplicationIcon(info.applicationInfo);
6051fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                if (DBG) Log.d(LOG_TAG, "Using app-specific icon");
6061fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            } catch (NameNotFoundException e) {
6071fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                icon = pm.getDefaultActivityIcon();
6081fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
6091fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            }
6101fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mAppIcon.setImageDrawable(icon);
6111fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mAppIcon.setVisibility(View.VISIBLE);
6121fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau            mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL,
6131fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingTop(),
6141fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingRight(),
6151fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau                    mSearchPlate.getPaddingBottom());
6161fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau        }
6171fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau    }
6181fd73239dcd2f17603e0819ca9963e04c8267bb2Mike LeBeau
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
620875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Setup the search "Badge" if requested by mode flags.
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateSearchBadge() {
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // assume both hidden
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int visibility = View.GONE;
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Drawable icon = null;
626875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        CharSequence text = null;
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // optionally show one or the other.
629a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert        if (mSearchable.useBadgeIcon()) {
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            visibility = View.VISIBLE;
632875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
633a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert        } else if (mSearchable.useBadgeLabel()) {
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            visibility = View.VISIBLE;
636875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBadgeLabel.setText(text);
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBadgeLabel.setVisibility(visibility);
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Update the hint in the query text field.
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateQueryHint() {
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isShowing()) {
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String hint = null;
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mSearchable != null) {
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int hintId = mSearchable.getHintId();
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (hintId != 0) {
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    hint = mActivityContext.getString(hintId);
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
656875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchAutoComplete.setHint(hint);
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Update the visibility of the voice button.  There are actually two voice search modes,
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * either of which will activate the button.
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateVoiceButton() {
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int visibility = View.GONE;
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable.getVoiceSearchEnabled()) {
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Intent testIntent = null;
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mSearchable.getVoiceSearchLaunchWebSearch()) {
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                testIntent = mVoiceWebSearchIntent;
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                testIntent = mVoiceAppSearchIntent;
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (testIntent != null) {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ResolveInfo ri = getContext().getPackageManager().
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ri != null) {
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    visibility = View.VISIBLE;
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVoiceButton.setVisibility(visibility);
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6835a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau
6845a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau    /**
6855a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau     * Hack to determine whether this is the browser, so we can remove the browser icon
6865a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau     * to the left of the search field, as a special requirement for Donut.
6875a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau     *
6885a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau     * TODO: For Eclair, reconcile this with the rest of the global search UI.
6895a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau     */
6905a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau    private boolean isBrowserSearch() {
6915a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau        return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
6925a7d752b65edc83abc1eeef462b8bf01038ebdffMike LeBeau    }
693f4acf39d99d105e1e4fc7c43fa54377bb2f262aeBjorn Bringert
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Listeners of various types
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
699875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * {@link Dialog#onTouchEvent(MotionEvent)} will cancel the dialog only when the
700875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * touch is outside the window. But the window includes space for the drop-down,
701875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * so we also cancel on taps outside the search bar when the drop-down is not showing.
702875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
703875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    @Override
704875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public boolean onTouchEvent(MotionEvent event) {
705875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // cancel if the drop-down is not showing and the touch event was outside the search plate
706875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (!mSearchAutoComplete.isPopupShowing() && isOutOfBounds(mSearchPlate, event)) {
707875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG) Log.d(LOG_TAG, "Pop-up not showing and outside of search plate.");
708875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            cancel();
709875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return true;
710875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
711875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Let Dialog handle events outside the window while the pop-up is showing.
712875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return super.onTouchEvent(event);
713875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
714875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
715875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private boolean isOutOfBounds(View v, MotionEvent event) {
716875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        final int x = (int) event.getX();
717875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        final int y = (int) event.getY();
718875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
719875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return (x < -slop) || (y < -slop)
720875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                || (x > (v.getWidth()+slop))
721875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                || (y > (v.getHeight()+slop));
722875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
723875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
724875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Dialog's OnKeyListener implements various search-specific functionality
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param keyCode This is the keycode of the typed key, and is the same value as
728875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        found in the KeyEvent parameter.
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param event The complete event record for the typed key
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Return true if the event was handled here, or false if not.
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
735875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (DBG) Log.d(LOG_TAG, "onKeyDown(" + keyCode + "," + event + ")");
736ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert        if (mSearchable == null) {
737ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert            return false;
738ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert        }
739ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert
7403c5767d8fea785ec8d4ad6de96032cdfe752f5daBryan Mawhinney        if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) {
7413c5767d8fea785ec8d4ad6de96032cdfe752f5daBryan Mawhinney            event.startTracking();
74283fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            // Consume search key for later use.
74383fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            return true;
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
745875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
746875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // if it's an action specified by the searchable activity, launch the
747875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // entered query with the action key
748875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
749a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert        if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
750a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert            launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
751875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return true;
752875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
75387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
75487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        return super.onKeyDown(keyCode, event);
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
756875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
75783fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn    @Override
75883fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn    public boolean onKeyUp(int keyCode, KeyEvent event) {
75983fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        if (DBG) Log.d(LOG_TAG, "onKeyUp(" + keyCode + "," + event + ")");
76083fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        if (mSearchable == null) {
76183fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            return false;
76283fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        }
76383fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn
76483fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking()
76583fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn                && !event.isCanceled()) {
76683fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            // If the search key is pressed, toggle between global and in-app search. If we are
76783fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            // currently doing global search and there is no in-app search context to toggle to,
76883fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            // just don't do anything.
76983fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn            return toggleGlobalSearch();
77083fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        }
77187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
77287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        return super.onKeyUp(keyCode, event);
77383fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn    }
77483fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Callback to watch the textedit field for empty/non-empty
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextWatcher mTextWatcher = new TextWatcher() {
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
780875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onTextChanged(CharSequence s, int start,
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int before, int after) {
784875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG_LOG_TIMING) {
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dbgLogTiming("onTextChanged()");
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
787ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert            if (mSearchable == null) {
788ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert                return;
789ee716fa2b03666622a73f8cc54dae4afd091353dBjorn Bringert            }
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            updateWidgetState();
791875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (!mSearchAutoComplete.isPerformingCompletion()) {
792875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // The user changed the query, remember it.
793875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                mUserQuery = s == null ? "" : s.toString();
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
797662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        public void afterTextChanged(Editable s) {
798b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn            if (mSearchable == null) {
799b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn                return;
800b06ea706530e6d19eb2a1a9a7ae6c5dd77d80af0Dianne Hackborn            }
801d21572cd446e56efe94689324107927ac2bffd2aSatish Sampath            if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) {
802662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                // The user changed the query, check if it is a URL and if so change the search
803662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                // button in the soft keyboard to the 'Go' button.
804662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION));
805662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                if (Regex.WEB_URL_PATTERN.matcher(mUserQuery).matches()) {
806662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    options = options | EditorInfo.IME_ACTION_GO;
807662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                } else {
808662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    options = options | EditorInfo.IME_ACTION_SEARCH;
809662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                }
810662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                if (options != mSearchAutoCompleteImeOptions) {
811662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    mSearchAutoCompleteImeOptions = options;
812662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    mSearchAutoComplete.setImeOptions(options);
813662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    // This call is required to update the soft keyboard UI with latest IME flags.
814662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType());
815662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                }
816662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            }
817662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        }
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enable/Disable the cancel button based on edit text state (any text?)
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateWidgetState() {
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // enable the button if we have one or more non-space characters
825875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        boolean enabled = !mSearchAutoComplete.isEmpty();
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton.setEnabled(enabled);
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mGoButton.setFocusable(enabled);
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * React to typing in the GO search button by refocusing to EditText.
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Continue typing the query.
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    View.OnKeyListener mButtonsKeyListener = new View.OnKeyListener() {
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onKey(View v, int keyCode, KeyEvent event) {
836875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // guard against possible race conditions
837875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (mSearchable == null) {
838875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return false;
839875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
840875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
841875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (!event.isSystem() &&
842875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
843875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
844875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
845875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
846875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // restore focus and give key to EditText ...
847875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (mSearchAutoComplete.requestFocus()) {
848875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    return mSearchAutoComplete.dispatchKeyEvent(event);
849875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
851875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * React to a click in the GO button by launching a search.
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    View.OnClickListener mGoButtonClickListener = new View.OnClickListener() {
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onClick(View v) {
861875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // guard against possible race conditions
862875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (mSearchable == null) {
863875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return;
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
865875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            launchQuerySearch();
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * React to a click in the voice search button.
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() {
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onClick(View v) {
874875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // guard against possible race conditions
875875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (mSearchable == null) {
876875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return;
877875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
879c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau                // First stop the existing search before starting voice search, or else we'll end
880c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau                // up showing the search dialog again once we return to the app.
881c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau                ((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE)).
882c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau                        stopSearch();
883c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mSearchable.getVoiceSearchLaunchWebSearch()) {
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    getContext().startActivity(mVoiceWebSearchIntent);
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
887c5e7fe36fbb79ea888da636779318d3e430aa24cMike LeBeau                    Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent);
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    getContext().startActivity(appSearchIntent);
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (ActivityNotFoundException e) {
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Should not happen, since we check the availability of
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // voice search before showing the button. But just in case...
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.w(LOG_TAG, "Could not find voice search activity");
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create and return an Intent that can launch the voice search activity, perform a specific
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * voice transcription, and forward the results to the searchable activity.
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param baseIntent The voice app search intent to start from
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return A completely-configured intent ready to send to the voice search activity
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Intent createVoiceAppSearchIntent(Intent baseIntent) {
906260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau        ComponentName searchActivity = mSearchable.getSearchActivity();
907260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // create the necessary intent to set up a search-and-forward operation
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // in the voice search system.   We have to keep the bundle separate,
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // because it becomes immutable once it enters the PendingIntent
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
912260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau        queryIntent.setComponent(searchActivity);
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PendingIntent pending = PendingIntent.getActivity(
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Now set up the bundle that will be inserted into the pending intent
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // when it's time to do the search.  We always build it here (even if empty)
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // because the voice search activity will always need to insert "QUERY" into
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // it anyway.
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Bundle queryExtras = new Bundle();
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mAppSearchData != null) {
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData);
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Now build the intent to launch the voice search.  Add all necessary
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // extras to launch the voice recognizer, and then all the necessary extras
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // to forward the results to the searchable activity
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Intent voiceIntent = new Intent(baseIntent);
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Add all of the configuration options supplied by the searchable's metadata
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String prompt = null;
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String language = null;
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int maxResults = 1;
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Resources resources = mActivityContext.getResources();
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable.getVoiceLanguageModeId() != 0) {
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable.getVoicePromptTextId() != 0) {
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            prompt = resources.getString(mSearchable.getVoicePromptTextId());
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable.getVoiceLanguageId() != 0) {
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            language = resources.getString(mSearchable.getVoiceLanguageId());
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSearchable.getVoiceMaxResults() != 0) {
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            maxResults = mSearchable.getVoiceMaxResults();
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
952260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau        voiceIntent.putExtra(EXTRA_CALLING_PACKAGE,
953260dfb5716eab1dfe9dcb93b3e658cefaad197f9Mike LeBeau                searchActivity == null ? null : searchActivity.toShortString());
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Add the values that configure forwarding the results
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return voiceIntent;
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
963662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     * Corrects http/https typo errors in the given url string, and if the protocol specifier was
964662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     * not present defaults to http.
965662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     *
966662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     * @param inUrl URL to check and fix
967662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     * @return fixed URL string.
968662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath     */
969662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath    private String fixUrl(String inUrl) {
970662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        if (inUrl.startsWith("http://") || inUrl.startsWith("https://"))
971662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            return inUrl;
972662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
973662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) {
974662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) {
975662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                inUrl = inUrl.replaceFirst("/", "//");
976662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            } else {
977662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                inUrl = inUrl.replaceFirst(":", "://");
978662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            }
979662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        }
980662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
981662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        if (inUrl.indexOf("://") == -1) {
982662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath            inUrl = "http://" + inUrl;
983662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        }
984662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
985662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath        return inUrl;
986662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath    }
987662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
988662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath    /**
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * React to the user typing "enter" or other hardwired keys while typing in the search box.
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This handles these special keys while the edit box has focus.
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onKey(View v, int keyCode, KeyEvent event) {
994875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // guard against possible race conditions
995875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (mSearchable == null) {
996875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return false;
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG_LOG_TIMING) dbgLogTiming("doTextKey()");
1000875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG) {
1001875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event
1002875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        + "), selection: " + mSearchAutoComplete.getListSelection());
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1004875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1005875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // If a suggestion is selected, handle enter, search key, and action keys
1006875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // as presses on the selected suggestion
1007875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (mSearchAutoComplete.isPopupShowing() &&
1008875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    mSearchAutoComplete.getListSelection() != ListView.INVALID_POSITION) {
1009875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return onSuggestionsKey(v, keyCode, event);
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1011875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1012875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // If there is text in the query box, handle enter, and action keys
1013875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // The search key is handled by the dialog's onKeyDown().
1014875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (!mSearchAutoComplete.isEmpty()) {
1015875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (keyCode == KeyEvent.KEYCODE_ENTER
1016875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        && event.getAction() == KeyEvent.ACTION_UP) {
1017875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    v.cancelLongPress();
1018662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath
1019b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                    // If this is a url entered by the user & we displayed the 'Go' button which
1020b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                    // the user clicked, launch the url instead of using it as a search query.
1021b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                    if (mSearchable.autoUrlDetect() &&
1022b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        (mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION)
1023d21572cd446e56efe94689324107927ac2bffd2aSatish Sampath                                == EditorInfo.IME_ACTION_GO) {
1024b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString()));
1025b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
1026b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1027b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        launchIntent(intent);
1028b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                    } else {
1029b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        // Launch as a regular search.
1030b1665f2e15439f693b1ef75cb1c07593f99edde7Satish Sampath                        launchQuerySearch();
1031662df0b9630fd685a7cf86bbfec15ac6b6c286c6Satish Sampath                    }
1032875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    return true;
1033875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
1034875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (event.getAction() == KeyEvent.ACTION_DOWN) {
1035875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
1036a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert                    if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
1037a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert                        launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
1038875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        return true;
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1042875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return false;
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1046a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn    @Override
1047ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen    public void hide() {
1048444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert        if (!isShowing()) return;
1049444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8Bjorn Bringert
1050a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn        // We made sure the IME was displayed, so also make sure it is closed
1051a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn        // when we go away.
1052a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn        InputMethodManager imm = (InputMethodManager)getContext()
1053a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn                .getSystemService(Context.INPUT_METHOD_SERVICE);
1054a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn        if (imm != null) {
1055a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn            imm.hideSoftInputFromWindow(
1056a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn                    getWindow().getDecorView().getWindowToken(), 0);
1057a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn        }
1058ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen
1059ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen        super.hide();
1060a8f556ee8cee24674663fd73c7a5b5a919b2d5bbDianne Hackborn    }
1061ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1063875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * React to the user typing while in the suggestions list. First, check for action
1064875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * keys. If not handled, try refocusing regular characters into the EditText.
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1066875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
1067875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // guard against possible race conditions (late arrival after dismiss)
1068875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (mSearchable == null) {
1069875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return false;
1070875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSuggestionsAdapter == null) {
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (event.getAction() == KeyEvent.ACTION_DOWN) {
1075875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (DBG_LOG_TIMING) {
1076875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                dbgLogTiming("onSuggestionsKey()");
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // First, check for enter or search (both of which we'll treat as a "click")
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
1081875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                int position = mSearchAutoComplete.getListSelection();
1082875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                return launchSuggestion(position);
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Next, check for left/right moves, which we use to "return" the user to the edit view
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
1087875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // give "focus" to text editor, with cursor at the beginning if
1088875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // left key, at end if right key
1089875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // TODO: Reverse left/right for right-to-left languages, e.g. Arabic
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ?
1091875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        0 : mSearchAutoComplete.length();
1092875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                mSearchAutoComplete.setSelection(selPoint);
1093875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                mSearchAutoComplete.setListSelection(0);
1094875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                mSearchAutoComplete.clearListSelection();
1095ffe3ecf2b1ee04288008758c0f60ae22238797c1Mike LeBeau                mSearchAutoComplete.ensureImeVisible();
1096ffe3ecf2b1ee04288008758c0f60ae22238797c1Mike LeBeau
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Next, check for an "up and out" move
1101875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (keyCode == KeyEvent.KEYCODE_DPAD_UP
1102875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    && 0 == mSearchAutoComplete.getListSelection()) {
1103875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                restoreUserQuery();
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // let ACTV complete the move
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Next, check for an "action key"
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if ((actionKey != null) &&
1111a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert                    ((actionKey.getSuggestActionMsg() != null) ||
1112a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert                     (actionKey.getSuggestActionMsgColumn() != null))) {
1113875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // launch suggestion using action key column
1114875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                int position = mSearchAutoComplete.getListSelection();
1115875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (position != ListView.INVALID_POSITION) {
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Cursor c = mSuggestionsAdapter.getCursor();
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c.moveToPosition(position)) {
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final String actionMsg = getActionKeyMessage(c, actionKey);
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (actionMsg != null && (actionMsg.length() > 0)) {
1120875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                            return launchSuggestion(position, keyCode, actionMsg);
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
1127875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1128875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1129875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1130875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Launch a search for the text in the query text field.
1131875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1132d2d6014f715f12f6263f61ba3eeb6f8cba6d0fa6krosaen    public void launchQuerySearch()  {
1133875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
1134875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1137875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Launch a search for the text in the query text field.
1138875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1139875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionKey The key code of the action key that was pressed,
1140875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
1141875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionMsg The message for the action key that was pressed,
1142875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or <code>null</code> if none.
1143875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1144875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    protected void launchQuerySearch(int actionKey, String actionMsg)  {
1145875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        String query = mSearchAutoComplete.getText().toString();
11464899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH;
11474899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        Intent intent = createIntent(action, null, null, query, null,
1148be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert                actionKey, actionMsg, null);
11492fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert        // Allow GlobalSearch to log and create shortcut for searches launched by
11502fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert        // the search button, enter key or an action key.
11512fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert        if (mGlobalSearchMode) {
11522fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert            mSuggestionsAdapter.reportSearch(query);
11532fcaf79a1856186e3823390c488e806c5955c8a6Bjorn Bringert        }
1154875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        launchIntent(intent);
1155875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1156875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1157875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1158875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Launches an intent based on a suggestion.
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1160875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param position The index of the suggestion to create the intent from.
1161875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return true if a successful launch, false if could not (e.g. bad position).
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1163875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    protected boolean launchSuggestion(int position) {
1164875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
1165875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1166875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1167875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1168875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Launches an intent based on a suggestion.
1169875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1170875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param position The index of the suggestion to create the intent from.
1171875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionKey The key code of the action key that was pressed,
1172875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
1173875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionMsg The message for the action key that was pressed,
1174875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or <code>null</code> if none.
1175875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return true if a successful launch, false if could not (e.g. bad position).
1176875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1177875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    protected boolean launchSuggestion(int position, int actionKey, String actionMsg) {
1178875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        Cursor c = mSuggestionsAdapter.getCursor();
1179875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if ((c != null) && c.moveToPosition(position)) {
1180a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1181875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
1182d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1183d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            // report back about the click
1184d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            if (mGlobalSearchMode) {
1185d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                // in global search mode, do it via cursor
11865417f09c141713c62be697fe10fe149d6d3d0eb4Bjorn Bringert                mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg);
1187d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            } else if (intent != null
1188d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    && mPreviousComponents != null
1189d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    && !mPreviousComponents.isEmpty()) {
1190d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                // in-app search (and we have pivoted in as told by mPreviousComponents,
1191d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                // which is used for keeping track of what we pop back to when we are pivoting into
1192d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                // in app search.)
1193d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                reportInAppClickToGlobalSearch(c, intent);
1194d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            }
1195d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1196d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            // launch the intent
1197875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            launchIntent(intent);
1198a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1199875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return true;
1200875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1201875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return false;
1202875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1203a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1204875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1205d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * Report a click from an in app search result back to global search for shortcutting porpoises.
1206d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     *
1207d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * @param c The cursor that is pointing to the clicked position.
1208d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * @param intent The intent that will be launched for the click.
1209d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     */
1210d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) {
1211d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // for in app search, still tell global search via content provider
1212d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        Uri uri = getClickReportingUri();
1213d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        final ContentValues cv = new ContentValues();
1214d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery);
1215d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        final ComponentName source = mSearchable.getSearchActivity();
1216d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString());
1217d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1218d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // grab the intent columns from the intent we created since it has additional
1219d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // logic for falling back on the searchable default
1220d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction());
1221d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString());
1222bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath        cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME,
12230e62e2df369b1f5a8e8ed38d3a6af6f568caf5e4Bjorn Bringert                intent.getComponent().flattenToShortString());
1224d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1225d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // ensure the icons will work for global search
1226d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_ICON_1,
1227d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                        wrapIconForPackage(
122883dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert                                mSearchable.getSuggestPackage(),
1229d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                                getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1)));
1230d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_ICON_2,
1231d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                        wrapIconForPackage(
123283dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert                                mSearchable.getSuggestPackage(),
1233d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                                getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2)));
1234d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1235d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // the rest can be passed through directly
1236d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_FORMAT,
1237d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT));
1238d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
1239d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1));
1240d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2,
1241d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2));
1242d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_QUERY,
1243d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY));
1244d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
1245d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID));
1246d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // note: deliberately omitting background color since it is only for global search
1247d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        // "more results" entries
1248d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        mContext.getContentResolver().insert(uri, cv);
1249d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    }
1250d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1251d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    /**
1252d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * @return A URI appropriate for reporting a click.
1253d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     */
1254d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    private Uri getClickReportingUri() {
1255d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        Uri.Builder uriBuilder = new Uri.Builder()
1256d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                .scheme(ContentResolver.SCHEME_CONTENT)
1257d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY);
1258d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1259d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH);
1260d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1261d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        return uriBuilder
1262d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                .query("")     // TODO: Remove, workaround for a bug in Uri.writeToParcel()
1263d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                .fragment("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
1264d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                .build();
1265d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    }
1266d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1267d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    /**
1268d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * Wraps an icon for a particular package.  If the icon is a resource id, it is converted into
1269d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * an android.resource:// URI.
1270d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     *
127183dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert     * @param packageName The source of the icon
1272d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * @param icon The icon retrieved from a suggestion column
1273d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * @return An icon string appropriate for the package.
1274d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     */
127583dba19a8e9d5be6f18a4c9e2e254386c25c66feBjorn Bringert    private String wrapIconForPackage(String packageName, String icon) {
1276d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        if (icon == null || icon.length() == 0 || "0".equals(icon)) {
1277d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            // SearchManager specifies that null or zero can be returned to indicate
1278d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            // no icon. We also allow empty string.
1279d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            return null;
1280d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        } else if (!Character.isDigit(icon.charAt(0))){
1281d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            return icon;
1282d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        } else {
1283d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen            return new Uri.Builder()
1284d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
1285d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    .authority(packageName)
1286d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    .encodedPath(icon)
1287d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen                    .toString();
1288d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen        }
1289d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    }
1290d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen
1291d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen    /**
1292ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen     * Launches an intent, including any special intent handling.  Doesn't dismiss the dialog
1293ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen     * since that will be handled in {@link SearchDialogWrapper#performActivityResuming}
1294875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1295875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void launchIntent(Intent intent) {
1296875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (intent == null) {
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1299875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (handleSpecialIntent(intent)){
1300875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return;
1301875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1302ea52d29bc46c306f3607d121aa1ad84f0e9eb473Karl Rosaen        Log.d(LOG_TAG, "launching " + intent);
13034899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        try {
13044899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // in global search mode, we send the activity straight to the original suggestion
13054899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // source. this is because GlobalSearch may not have permission to launch the
13064899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // intent, and to avoid the extra step of going through GlobalSearch.
13074899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            if (mGlobalSearchMode) {
130842b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                if (mStoredComponentName != null) {
130942b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                    // If we're embedded in an application, dismiss the dialog.
131042b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                    // This ensures that if the intent is handled by the current
131142b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                    // activity, it's not obscured by the dialog.
131242b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                    dismiss();
131342b7af57c59d8b7cb9be3fea713478424b53b7fcBryan Mawhinney                }
13144899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                launchGlobalSearchIntent(intent);
13154899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            } else {
1316725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert                // If the intent was created from a suggestion, it will always have an explicit
1317725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert                // component here.
1318725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert                Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
13194899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                getContext().startActivity(intent);
1320da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                // If the search switches to a different activity,
1321da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                // SearchDialogWrapper#performActivityResuming
13224899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                // will handle hiding the dialog when the next activity starts, but for
1323da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                // real in-app search, we still need to dismiss the dialog.
1324da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                if (isInRealAppSearch()) {
1325da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                    dismiss();
1326da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert                }
13274899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            }
13284899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        } catch (RuntimeException ex) {
13294899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
13304899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
13314899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    }
1332876627d8c52300608c8c716f980a8686f43bf476Karl Rosaen
13334899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    private void launchGlobalSearchIntent(Intent intent) {
13344899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        final String packageName;
13354899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // GlobalSearch puts the original source of the suggestion in the
13364899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // 'component name' column. If set, we send the intent to that activity.
13374899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // We trust GlobalSearch to always set this to the suggestion source.
13384899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY);
13394899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        if (intentComponent != null) {
13404899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            ComponentName componentName = ComponentName.unflattenFromString(intentComponent);
13414899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            intent.setComponent(componentName);
13424899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            intent.removeExtra(SearchManager.COMPONENT_NAME_KEY);
13434899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // Launch the intent as the suggestion source.
13444899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // This prevents sources from using the search dialog to launch
13454899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // intents that they don't have permission for themselves.
13464899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            packageName = componentName.getPackageName();
13474899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        } else {
13484899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // If there is no component in the suggestion, it must be a built-in suggestion
13494899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // from GlobalSearch (e.g. "Search the web for") or the intent
13504899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // launched when pressing the search/go button in the search dialog.
13514899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            // Launch the intent with the permissions of GlobalSearch.
13524899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            packageName = mSearchable.getSearchActivity().getPackageName();
1353876627d8c52300608c8c716f980a8686f43bf476Karl Rosaen        }
13544899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
13554899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // Launch all global search suggestions as new tasks, since they don't relate
13564899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // to the current task.
13574899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
13584899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        setBrowserApplicationId(intent);
13594899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
13604899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        startActivityInPackage(intent, packageName);
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13624899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
13634899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    /**
13644899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * If the intent is to open an HTTP or HTTPS URL, we set
13654899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that
13664899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * has been opened by us for the same URL will be reused.
13674899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     */
13684899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    private void setBrowserApplicationId(Intent intent) {
13694899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        Uri data = intent.getData();
13704899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null) {
13714899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            String scheme = data.getScheme();
13724899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            if (scheme != null && scheme.startsWith("http")) {
13734899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                intent.putExtra(Browser.EXTRA_APPLICATION_ID, data.toString());
13744899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            }
13754899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
13764899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    }
13774899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
13784899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    /**
13794899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * Starts an activity as if it had been started by the given package.
13804899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     *
13814899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * @param intent The description of the activity to start.
13824899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * @param packageName
13834899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * @throws ActivityNotFoundException If the intent could not be resolved to
13844899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     *         and existing activity.
13854899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * @throws SecurityException If the package does not have permission to start
13864899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     *         start the activity.
13874899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     * @throws AndroidRuntimeException If some other error occurs.
13884899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert     */
13894899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    private void startActivityInPackage(Intent intent, String packageName) {
13904899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        try {
13914899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            int uid = ActivityThread.getPackageManager().getPackageUid(packageName);
13924899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            if (uid < 0) {
13934899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                throw new AndroidRuntimeException("Package UID not found " + packageName);
13944899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            }
13954899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver());
13964899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            IBinder resultTo = null;
13974899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            String resultWho = null;
13984899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            int requestCode = -1;
13994899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            boolean onlyIfNeeded = false;
1400725d8ff6c5b4e27c8b7f2969ce1ddf6ec715ea6dBjorn Bringert            Log.i(LOG_TAG, "Starting (uid " + uid + ", " + packageName + ") " + intent.toURI());
14014899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            int result = ActivityManagerNative.getDefault().startActivityInPackage(
14024899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                    uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded);
14034899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            checkStartActivityResult(result, intent);
14044899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        } catch (RemoteException ex) {
14054899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            throw new AndroidRuntimeException(ex);
14064899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
14074899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    }
14084899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
14094899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    // Stolen from Instrumentation.checkStartActivityResult()
14104899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    private static void checkStartActivityResult(int res, Intent intent) {
14114899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        if (res >= IActivityManager.START_SUCCESS) {
14124899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            return;
14134899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
14144899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        switch (res) {
14154899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            case IActivityManager.START_INTENT_NOT_RESOLVED:
14164899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            case IActivityManager.START_CLASS_NOT_FOUND:
14174899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                if (intent.getComponent() != null)
14184899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                    throw new ActivityNotFoundException(
14194899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                            "Unable to find explicit activity class "
14204899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                            + intent.getComponent().toShortString()
14214899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                            + "; have you declared this activity in your AndroidManifest.xml?");
14224899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                throw new ActivityNotFoundException(
14234899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                        "No Activity found to handle " + intent);
14244899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            case IActivityManager.START_PERMISSION_DENIED:
14254899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                throw new SecurityException("Not allowed to start activity "
14264899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                        + intent);
14274899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
14284899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                throw new AndroidRuntimeException(
14294899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                        "FORWARD_RESULT_FLAG used while also requesting a result");
14304899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            default:
14314899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                throw new AndroidRuntimeException("Unknown error code "
14324899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert                        + res + " when starting " + intent);
14334899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
14344899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert    }
14354899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1437875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Handles the special intent actions declared in {@link SearchManager}.
1438875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1439875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return <code>true</code> if the intent was handled.
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1441875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private boolean handleSpecialIntent(Intent intent) {
1442875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        String action = intent.getAction();
1443875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) {
1444875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            handleChangeSourceIntent(intent);
1445875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return true;
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1447875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return false;
1448875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1449875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1450875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1451d4c98c4c450a95b67fe9746675984333b7653713Karl Rosaen     * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}.
1452875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1453875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void handleChangeSourceIntent(Intent intent) {
1454875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        Uri dataUri = intent.getData();
1455875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (dataUri == null) {
1456875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Log.w(LOG_TAG, "SearchManager.INTENT_ACTION_CHANGE_SOURCE without intent data.");
1457875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return;
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1459875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        ComponentName componentName = ComponentName.unflattenFromString(dataUri.toString());
1460875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (componentName == null) {
1461875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Log.w(LOG_TAG, "Invalid ComponentName: " + dataUri);
1462875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return;
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1464875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (DBG) Log.d(LOG_TAG, "Switching to " + componentName);
1465da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert
1466da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert        pushPreviousComponent(mLaunchComponent);
1467875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (!show(componentName, mAppSearchData, false)) {
1468875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Log.w(LOG_TAG, "Failed to switch to source " + componentName);
1469da1f4b669ead7813079643b43301f100127aaeedBjorn Bringert            popPreviousComponent();
1470875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return;
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1473875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        String query = intent.getStringExtra(SearchManager.QUERY);
1474875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        setUserQuery(query);
147535df87c6319863beed9fd95d0a279e34ff94e92eMike LeBeau        mSearchAutoComplete.showDropDown();
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1477a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1478875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1479ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau     * Sets the list item selection in the AutoCompleteTextView's ListView.
1480ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau     */
1481ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau    public void setListSelection(int index) {
1482ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau        mSearchAutoComplete.setListSelection(index);
1483ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau    }
1484a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1485ae9760b62c400e3e7009d4a7b85e9524a5cd44fcMike LeBeau    /**
148687d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert     * Checks if there are any previous searchable components in the history stack.
148787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert     */
148887d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    private boolean hasPreviousComponent() {
148987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        return mPreviousComponents != null && !mPreviousComponents.isEmpty();
149087d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    }
149187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
149287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    /**
1493875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Saves the previous component that was searched, so that we can go
1494875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * back to it.
1495875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1496875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private void pushPreviousComponent(ComponentName componentName) {
1497875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (mPreviousComponents == null) {
1498875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mPreviousComponents = new ArrayList<ComponentName>();
1499875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1500875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mPreviousComponents.add(componentName);
1501875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1502875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1503875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1504875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Pops the previous component off the stack and returns it.
1505875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1506875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return The component name, or <code>null</code> if there was
1507875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *         no previous component.
1508875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1509875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private ComponentName popPreviousComponent() {
151087d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        if (!hasPreviousComponent()) {
1511875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return null;
1512875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
151387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        return mPreviousComponents.remove(mPreviousComponents.size() - 1);
1514875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1515875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1516875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
1517875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Goes back to the previous component that was searched, if any.
1518875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1519875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return <code>true</code> if there was a previous component that we could go back to.
1520875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
152187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    private boolean backToPreviousComponent() {
1522875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        ComponentName previous = popPreviousComponent();
1523875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (previous == null) {
1524875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return false;
1525875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
152687d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
152787d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        if (!show(previous, mAppSearchData, false)) {
152887d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert            Log.w(LOG_TAG, "Failed to switch to source " + previous);
152987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert            return false;
153083fe3f559249451706957b1a5f660b2b8272f114Dianne Hackborn        }
153187d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
153287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        // must touch text to trigger suggestions
153387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        // TODO: should this be the text as it was when the user left
153487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        // the source that we are now going back to?
153587d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        String query = mSearchAutoComplete.getText().toString();
153687d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        setUserQuery(query);
1537875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return true;
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * When a particular suggestion has been selected, perform the various lookups required
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to use the suggestion.  This includes checking the cursor for suggestion-specific data,
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and/or falling back to the XML for defaults;  It also creates REST style Uri data when
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the suggestion includes a data id.
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
15469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param c The suggestions cursor, moved to the row of the user's selection
1547875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionKey The key code of the action key that was pressed,
1548875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
1549875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionMsg The message for the action key that was pressed,
1550875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or <code>null</code> if none.
1551875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return An intent for the suggestion at the cursor's position.
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1553875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // use specific action if supplied, or default action if supplied, or fixed default
1556875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
1557a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1558a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen            // some items are display only, or have effect via the cursor respond click reporting.
1559a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen            if (SearchManager.INTENT_ACTION_NONE.equals(action)) {
1560a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen                return null;
1561a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen            }
1562a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1563875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (action == null) {
1564875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                action = mSearchable.getSuggestIntentAction();
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1566875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (action == null) {
1567875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                action = Intent.ACTION_SEARCH;
15689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // use specific data if supplied, or default data if supplied
1571875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (data == null) {
1573875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                data = mSearchable.getSuggestIntentData();
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // then, if an ID was provided, append it.
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (data != null) {
1577875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
1578875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (id != null) {
1579875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    data = data + "/" + Uri.encode(id);
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1582875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Uri dataUri = (data == null) ? null : Uri.parse(data);
1583875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1584bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath            String componentName = getColumnString(
1585bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath                    c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
1586a058f02b591d971a829cb1e28d48a992e46ad85eKarl Rosaen
1587875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
1588bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath            String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
1589be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert            String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null;
1590875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1591bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath            return createIntent(action, dataUri, extraData, query, componentName, actionKey,
1592be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert                    actionMsg, mode);
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RuntimeException e ) {
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int rowNum;
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {                       // be really paranoid now
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                rowNum = c.getPosition();
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RuntimeException e2 ) {
15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                rowNum = -1;
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            " returned exception" + e.toString());
1602875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return null;
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1607875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Constructs an intent from the given information and the search dialog state.
1608875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
1609875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param action Intent action.
1610875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param data Intent data, or <code>null</code>.
1611875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
1612bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath     * @param query Intent query, or <code>null</code>.
1613bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath     * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>.
1614875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionKey The key code of the action key that was pressed,
1615875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
1616875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param actionMsg The message for the action key that was pressed,
1617875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *        or <code>null</code> if none.
1618be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert     * @param mode The search mode, one of the acceptable values for
1619be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert     *             {@link SearchManager#SEARCH_MODE}, or {@code null}.
1620875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return The intent.
1621875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
1622bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath    private Intent createIntent(String action, Uri data, String extraData, String query,
1623be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert            String componentName, int actionKey, String actionMsg, String mode) {
1624875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Now build the Intent
1625875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        Intent intent = new Intent(action);
16268d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1627442da48b24f75e6075763b89943b513197a0bfe9Bjorn Bringert        // We need CLEAR_TOP to avoid reusing an old task that has other activities
1628442da48b24f75e6075763b89943b513197a0bfe9Bjorn Bringert        // on top of the one we want.
1629442da48b24f75e6075763b89943b513197a0bfe9Bjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1630875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (data != null) {
1631875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.setData(data);
1632875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
16335f80605a0b866f34c74955e190909bd6ab5d992eBjorn Bringert        intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
1634875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (query != null) {
1635875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.putExtra(SearchManager.QUERY, query);
1636875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1637875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (extraData != null) {
1638875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
1639875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1640bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath        if (componentName != null) {
1641bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath            intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName);
1642bf23fe062ab4321143717c21827d329e087cc72fSatish Sampath        }
1643875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (mAppSearchData != null) {
1644875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
1645875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1646875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
1647875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.putExtra(SearchManager.ACTION_KEY, actionKey);
1648875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
1649875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
1650be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert        if (mode != null) {
1651be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert            intent.putExtra(SearchManager.SEARCH_MODE, mode);
1652be5b73c5926cbebd508d8323bdeaafa2e048a93cBjorn Bringert        }
16534899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        // Only allow 3rd-party intents from GlobalSearch
16544899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        if (!mGlobalSearchMode) {
16554899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert            intent.setComponent(mSearchable.getSearchActivity());
16564899e386ff9541e066a37cb5e9449844ded5ae4eBjorn Bringert        }
1657875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return intent;
1658875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
1659875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1660875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * For a given suggestion and a given cursor row, get the action message.  If not provided
16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by the specific row/column, also check for a single definition (for the action key).
16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
16649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param c The cursor providing suggestions
16659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param actionKey The actionkey record being examined
16669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
16679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Returns a string, or null if no action key message for this suggestion
16689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1669875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
16709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String result = null;
16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // check first in the cursor data, for a suggestion-specific message
1672a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert        final String column = actionKey.getSuggestActionMsgColumn();
16739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (column != null) {
1674875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            result = SuggestionsAdapter.getColumnString(c, column);
16759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // If the cursor didn't give us a message, see if there's a single message defined
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // for the actionkey (for all suggestions)
16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (result == null) {
1679a92041306e4d73baa10fb711fb905b9590f06b26Bjorn Bringert            result = actionKey.getSuggestActionMsg();
16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
16829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1683077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1684077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert    /**
1685077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert     * The root element in the search bar layout. This is a custom view just to override
1686077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert     * the handling of the back button.
1687077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert     */
1688077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert    public static class SearchBar extends LinearLayout {
1689077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1690077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        private SearchDialog mSearchDialog;
1691077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1692077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        public SearchBar(Context context, AttributeSet attrs) {
1693077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            super(context, attrs);
1694077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        }
1695077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1696077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        public SearchBar(Context context) {
1697077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            super(context);
1698077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        }
1699077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1700077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        public void setSearchDialog(SearchDialog searchDialog) {
1701077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            mSearchDialog = searchDialog;
1702077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        }
1703077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
1704077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        /**
1705077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert         * Overrides the handling of the back key to move back to the previous sources or dismiss
1706077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert         * the search dialog, instead of dismissing the input method.
1707077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert         */
1708077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        @Override
1709077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        public boolean dispatchKeyEventPreIme(KeyEvent event) {
1710077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + event + ")");
1711077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
1712077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                if (event.getAction() == KeyEvent.ACTION_DOWN
1713077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                        && event.getRepeatCount() == 0) {
1714077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                    getKeyDispatcherState().startTracking(event, this);
1715077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                    return true;
1716077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                } else if (event.getAction() == KeyEvent.ACTION_UP
1717077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                        && !event.isCanceled() && getKeyDispatcherState().isTracking(event)) {
1718077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                    mSearchDialog.onBackPressed();
1719077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                    return true;
1720077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert                }
1721077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            }
1722077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert            return super.dispatchKeyEventPreIme(event);
1723077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert        }
1724077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert    }
1725077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1727875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Local subclass for AutoCompleteTextView.
17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
17299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class SearchAutoComplete extends AutoCompleteTextView {
17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1731875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        private int mThreshold;
1732077357ccf0c8b35094a69c3fc8bddd9ee605cf78Bjorn Bringert
17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SearchAutoComplete(Context context) {
17341746d6fefcb1b4869f475a81f5aec35db4695603Marco Nelissen            super(context);
1735875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mThreshold = getThreshold();
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SearchAutoComplete(Context context, AttributeSet attrs) {
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(context, attrs);
1740875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mThreshold = getThreshold();
17419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
17449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(context, attrs, defStyle);
1745875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mThreshold = getThreshold();
17469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1747875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
17489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1749875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        public void setThreshold(int threshold) {
1750875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            super.setThreshold(threshold);
1751875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mThreshold = threshold;
17529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1755875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         * Returns true if the text field is empty, or contains only whitespace.
17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1757875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        private boolean isEmpty() {
1758875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return TextUtils.getTrimmedLength(getText()) == 0;
17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17608d17f3f24bbda9a9cd7ea08c5925508dc2c011beBjorn Bringert
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1762875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         * We override this method to avoid replacing the query box text
1763875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         * when a suggestion is clicked.
17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1766875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        protected void replaceText(CharSequence text) {
17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1770617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau         * We override this method to avoid an extra onItemClick being called on the
1771617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau         * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)}
1772617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau         * when an item is clicked with the trackball.
1773617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau         */
1774617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau        @Override
1775617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau        public void performCompletion() {
1776617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau        }
17771fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau
17781fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau        /**
17791fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau         * We override this method to be sure and show the soft keyboard if appropriate when
17801fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau         * the TextView has focus.
17811fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau         */
17821fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau        @Override
17831fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau        public void onWindowFocusChanged(boolean hasWindowFocus) {
17841fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau            super.onWindowFocusChanged(hasWindowFocus);
17851fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau
17861fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau            if (hasWindowFocus) {
17871fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau                InputMethodManager inputManager = (InputMethodManager)
17881fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
17891fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau                inputManager.showSoftInput(this, 0);
17901fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau            }
17911fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau        }
17921fffbd97fa756ef7d3e7e885a5c1c8bb64380c20Mike LeBeau
1793617202a53a26d1b38a41ce69c40b1fffd784c178Mike LeBeau        /**
1794875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         * We override this method so that we can allow a threshold of zero, which ACTV does not.
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1797875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        public boolean enoughToFilter() {
1798875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            return mThreshold <= 0 || super.enoughToFilter();
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
180098e333f551a4bf2ebb50bb97a2a56b14bfdcd74bKarl Rosaen
1801875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
180287d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
180387d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    @Override
180487d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert    public void onBackPressed() {
180587d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert        if (!backToPreviousComponent()) {
180687d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert            cancel();
18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
180987d61f22e13e81550559c92f6cf6fb795a39d37fBjorn Bringert
18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Implements OnItemClickListener
18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1814875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position);
1815875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        launchSuggestion(position);
18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1817875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Implements OnItemSelectedListener
18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1822875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position);
1823875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         // A suggestion has been selected, rewrite the query if possible,
1824875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         // otherwise the restore the original query.
1825875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (REWRITE_QUERIES) {
1826875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             rewriteQueryFromSuggestion(position);
1827875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         }
18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     }
18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     /**
18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project      * Implements OnItemSelectedListener
18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project      */
18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     public void onNothingSelected(AdapterView<?> parent) {
1834875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (DBG) Log.d(LOG_TAG, "onNothingSelected()");
1835875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     }
1836875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1837875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     /**
1838875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      * Query rewriting.
1839875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      */
1840875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1841875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     private void rewriteQueryFromSuggestion(int position) {
1842875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         Cursor c = mSuggestionsAdapter.getCursor();
1843875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (c == null) {
1844875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             return;
1845875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         }
1846875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (c.moveToPosition(position)) {
1847875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             // Get the new query from the suggestion.
1848875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
1849875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             if (newQuery != null) {
1850875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 // The suggestion rewrites the query.
1851875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 if (DBG) Log.d(LOG_TAG, "Rewriting query to '" + newQuery + "'");
1852875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 // Update the text field, without getting new suggestions.
1853875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 setQuery(newQuery);
1854875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             } else {
1855875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 // The suggestion does not rewrite the query, restore the user's query.
1856875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 if (DBG) Log.d(LOG_TAG, "Suggestion gives no rewrite, restoring user query.");
1857875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                 restoreUserQuery();
1858875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             }
1859875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         } else {
1860875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             // We got a bad position, restore the user's query.
1861875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             Log.w(LOG_TAG, "Bad suggestion position: " + position);
1862875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             restoreUserQuery();
1863875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         }
1864875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     }
1865875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1866875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     /**
1867875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      * Restores the query entered by the user if needed.
1868875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      */
1869875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     private void restoreUserQuery() {
1870875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (DBG) Log.d(LOG_TAG, "Restoring query to '" + mUserQuery + "'");
1871875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         setQuery(mUserQuery);
1872875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     }
1873875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1874875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     /**
1875875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      * Sets the text in the query box, without updating the suggestions.
1876875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      */
1877875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     private void setQuery(CharSequence query) {
1878875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         mSearchAutoComplete.setText(query, false);
1879875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (query != null) {
1880875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             mSearchAutoComplete.setSelection(query.length());
1881875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         }
1882875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     }
1883875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1884875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     /**
1885875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      * Sets the text in the query box, updating the suggestions.
1886875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen      */
1887875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     private void setUserQuery(String query) {
1888875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         if (query == null) {
1889875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen             query = "";
1890875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         }
1891875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         mUserQuery = query;
1892875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         mSearchAutoComplete.setText(query);
1893875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen         mSearchAutoComplete.setSelection(query.length());
18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     }
18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Debugging Support
18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * For debugging only, sample the millisecond clock and log it.
19029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Uses AtomicLong so we can use in multiple threads
19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private AtomicLong mLastLogTime = new AtomicLong(SystemClock.uptimeMillis());
19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void dbgLogTiming(final String caller) {
19069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long millis = SystemClock.uptimeMillis();
19079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long oldTime = mLastLogTime.getAndSet(millis);
19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long delta = millis - oldTime;
19099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final String report = millis + " (+" + delta + ") ticks for Search keystroke in " + caller;
19109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.d(LOG_TAG,report);
19119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1913