SearchActivity.java revision 18959800eddb7127e4fd9b24e91458d0584cc257
13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
23e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
193f71a11f8858b9164ca83e8d2f558dd8a8a5e2deBjorn Bringertimport com.android.common.Search;
20c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringertimport com.android.quicksearchbox.ui.CorpusViewFactory;
21185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionClickListener;
227b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionSelectionListener;
23185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsAdapter;
24185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsView;
25185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
28dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringertimport android.content.DialogInterface;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
3021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringertimport android.database.DataSetObserver;
31d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinneyimport android.graphics.drawable.Drawable;
32fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
3411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport android.os.Debug;
354ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringertimport android.os.Handler;
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.Editable;
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextWatcher;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.KeyEvent;
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View.OnFocusChangeListener;
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
45145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringertimport android.widget.AbsListView;
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.EditText;
473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.ImageButton;
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport java.io.File;
50e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringertimport java.util.Collection;
5147d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringertimport java.util.List;
52ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
59b5fc08b7f16a32d3865f44b7f26d8aaa5304a2adBjorn Bringert    private static final boolean DBG = false;
600484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchActivity";
6111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    private static final boolean TRACE = false;
623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
63fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String SCHEME_CORPUS = "qsb.corpus";
64fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
65fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static final String INTENT_ACTION_QSB_AND_SELECT_CORPUS
66fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            = "com.android.quicksearchbox.action.QSB_AND_SELECT_CORPUS";
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
68c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // The string used for privateImeOptions to identify to the IME that it should not show
69c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // a microphone button since one already exists in the search dialog.
70c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // TODO: This should move to android-common or something.
71c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    private static final String IME_OPTION_NO_MICROPHONE = "nm";
72c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert
739ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
74fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String INSTANCE_KEY_CORPUS = "corpus";
759ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private static final String INSTANCE_KEY_USER_QUERY = "query";
76713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
77f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    // Measures time from for last onCreate()/onNewIntent() call.
78f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private LatencyTracker mStartLatencyTracker;
79ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
80ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
81ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
82ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
83ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
84ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
85f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private CorpusSelectionDialog mCorpusSelectionDialog;
86f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
87185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    protected SuggestionsAdapter mSuggestionsAdapter;
88185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
8921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private CorporaObserver mCorporaObserver;
9021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected EditText mQueryTextView;
92e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    // True if the query was empty on the previous call to updateQuery()
93e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected boolean mQueryWasEmpty = true;
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected SuggestionsView mSuggestionsView;
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mSearchGoButton;
983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mVoiceSearchButton;
99a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert    protected ImageButton mCorpusIndicator;
1003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
10181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    private VoiceSearch mVoiceSearch;
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
103fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus mCorpus;
1042617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
105713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mUpdateSuggestions;
106713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private String mUserQuery;
107713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mSelectAll;
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
109d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Handler mHandler = new Handler();
110d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mUpdateSuggestionsTask = new Runnable() {
1114ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        public void run() {
1124ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert            updateSuggestions(getQuery());
1134ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        }
1144ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    };
1154ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
116d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mShowInputMethodTask = new Runnable() {
117d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        public void run() {
118d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            showInputMethodForQuery();
119d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
120d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    };
121d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
12511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (TRACE) startMethodTracing();
126ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
129713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
130e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        setContentView(R.layout.search_activity);
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
132816804b67619af133860a1e28e92f58bc642260dBjorn Bringert        mSuggestionsAdapter = getQsbApplication().createSuggestionsAdapter();
133185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
1343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView = (EditText) findViewById(R.id.search_src_text);
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
136782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setSuggestionClickListener(new ClickHandler());
1371aef21f8a84380351c7dd7c9bf66f80659d0d42eMathew Inwood        mSuggestionsView.addSuggestionSelectionListener(new SelectionHandler());
138145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        mSuggestionsView.setOnScrollListener(new InputMethodCloser());
139713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
1407b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mSuggestionsView.setOnFocusChangeListener(new SuggestListFocusListener());
1413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
144a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator = (ImageButton) findViewById(R.id.corpus_indicator);
1453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
14681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        mVoiceSearch = new VoiceSearch(this);
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
148782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
149782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
1507b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
151782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
152fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnClickListener(new CorpusIndicatorClickListener());
1532617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
154782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
155782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
156782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
1577be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1587be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        ButtonsKeyListener buttonsKeyListener = new ButtonsKeyListener();
1597be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mSearchGoButton.setOnKeyListener(buttonsKeyListener);
1607be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mVoiceSearchButton.setOnKeyListener(buttonsKeyListener);
161fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnKeyListener(buttonsKeyListener);
162713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1639ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUpdateSuggestions = true;
1649ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1689ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1699ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1709ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
171713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
172713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
173713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setAdapter(mSuggestionsAdapter);
17421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
17521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mCorporaObserver = new CorporaObserver();
17621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().registerDataSetObserver(mCorporaObserver);
1779ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
178713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
17911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    private void startMethodTracing() {
18011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        File traceDir = getDir("traces", 0);
18111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        String traceFile = new File(traceDir, "qsb.trace").getAbsolutePath();
18211234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        Debug.startMethodTracing(traceFile);
18311234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    }
18411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert
1859ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1869ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
187ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1889ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1899ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1909ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
192ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
193f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mStartLatencyTracker = new LatencyTracker();
194ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
195ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
196ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
197ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
1989ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
1999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
200fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS);
2019ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
20221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
2039ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setUserQuery(query);
204713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
205713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
206713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
2079ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
2089ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
2099ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
2109ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
211fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
21221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        outState.putString(INSTANCE_KEY_CORPUS, getCorpusName());
2139ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        outState.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
214713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
215713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
216713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
217713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
21821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        String corpusName = getCorpusNameFromUri(intent.getData());
2192617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
2202617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
2212617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
22221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
2232617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        setUserQuery(query);
224154017089860da89ec020ed37e502d194253cc67Bjorn Bringert        mSelectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
225c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mAppSearchData = appSearchData;
2262617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
22711c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        if (startedIntoCorpusSelectionDialog()) {
22841fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
2292617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
2309ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
2319ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
23211c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    public boolean startedIntoCorpusSelectionDialog() {
23311c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        return INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(getIntent().getAction());
23411c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    }
23511c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert
236dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    /**
237dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * Removes corpus selector intent action, so that BACK works normally after
238dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * dismissing and reopening the corpus selector.
239dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     */
240dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    private void clearStartedIntoCorpusSelectionDialog() {
241dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        Intent oldIntent = getIntent();
242dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        if (SearchActivity.INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(oldIntent.getAction())) {
243dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            Intent newIntent = new Intent(oldIntent);
244dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            newIntent.setAction(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
245dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            setIntent(newIntent);
246dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
247dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
248dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
249fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static Uri getCorpusUri(Corpus corpus) {
250fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) return null;
251fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return new Uri.Builder()
252fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .scheme(SCHEME_CORPUS)
253fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .authority(corpus.getName())
254fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .build();
255fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
256fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
25721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusNameFromUri(Uri uri) {
258fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
259fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
26021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return uri.getAuthority();
261fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
262fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
263fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpus(String sourceName) {
2640ccab5903ba2c64ec4cdd33dbcea91b1e279d556Bjorn Bringert        if (sourceName == null) return null;
265fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpora().getCorpus(sourceName);
266fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
267fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            Log.w(TAG, "Unknown corpus " + sourceName);
2689ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
269713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
270fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return corpus;
271713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
272713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
27321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private void setCorpus(String corpusName) {
27421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        if (DBG) Log.d(TAG, "setCorpus(" + corpusName + ")");
27521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mCorpus = getCorpus(corpusName);
2769ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Drawable sourceIcon;
27721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        if (mCorpus == null) {
278c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert            sourceIcon = getCorpusViewFactory().getGlobalSearchIcon();
279713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        } else {
28021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            sourceIcon = mCorpus.getCorpusIcon();
281713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
28221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mSuggestionsAdapter.setCorpus(mCorpus);
283a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator.setImageDrawable(sourceIcon);
2843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2855691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateUi(getQuery().length() == 0);
2860484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
2870484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
28821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusName() {
28921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return mCorpus == null ? null : mCorpus.getName();
29021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
29121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
2923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
2933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (QsbApplication) getApplication();
2943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2964ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Config getConfig() {
2974ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        return getQsbApplication().getConfig();
2984ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
2994ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
300fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpora getCorpora() {
301fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getCorpora();
302fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
303fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
3043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private ShortcutRepository getShortcutRepository() {
3053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getShortcutRepository();
3063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
308ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
30947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        return getQsbApplication().getSuggestionsProvider();
31047d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
31147d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
31247d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    private CorpusRanker getCorpusRanker() {
31347d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        return getQsbApplication().getCorpusRanker();
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
316c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert    private CorpusViewFactory getCorpusViewFactory() {
317c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert        return getQsbApplication().getCorpusViewFactory();
318185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
319185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
320ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
321ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
322ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
323ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
3243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
3263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
3273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onDestroy();
32821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().unregisterDataSetObserver(mCorporaObserver);
329185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsView.setAdapter(null);  // closes mSuggestionsAdapter
3303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
3349ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
335f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (!mTookAction) {
336f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // TODO: This gets logged when starting other activities, e.g. by opening he search
337f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // settings, or clicking a notification in the status bar.
338f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logExit(getCurrentSuggestions(), getQuery().length());
339f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
3409ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
342185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
34394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        getQsbApplication().getShortcutRefresher().reset();
344f69873ae02e6d8f8a550f5fa43b5b1f2cbea96ffBjorn Bringert        dismissCorpusSelectionDialog();
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
349b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
3509ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
351b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
3529ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setQuery(mUserQuery, mSelectAll);
3539ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Only select everything the first time after creating the activity.
3549ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSelectAll = false;
35511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        updateSuggestionsBuffered();
356dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        if (!isCorpusSelectionDialogShowing()) {
357dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            mQueryTextView.requestFocus();
358dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
35911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (TRACE) Debug.stopMethodTracing();
360b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
361b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
362b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
3633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean onCreateOptionsMenu(Menu menu) {
3643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreateOptionsMenu(menu);
36526d85260316193a1aeff5d407d4f32f5297be012Bjorn Bringert        SearchSettings.addSearchSettingsMenuItem(this, menu);
3663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
3673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
369d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    @Override
370d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    public void onWindowFocusChanged(boolean hasFocus) {
371d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        super.onWindowFocusChanged(hasFocus);
372d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        if (hasFocus) {
373d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            // Launch the IME after a bit
374d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            mHandler.postDelayed(mShowInputMethodTask, 0);
375d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
376d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    }
377d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
3789ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    /**
3799ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * Sets the query as typed by the user. Does not update the suggestions
3809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * or the text in the query box.
3819ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     */
3829ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setUserQuery(String userQuery) {
3839ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (userQuery == null) userQuery = "";
3849ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUserQuery = userQuery;
3859ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
3869ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
387713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        CharSequence q = mQueryTextView.getText();
3893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return q == null ? "" : q.toString();
3903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Restores the query entered by the user.
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void restoreUserQuery() {
3963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Restoring query to '" + mUserQuery + "'");
3973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setQuery(mUserQuery, false);
3983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions,
4023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * and does not change the saved user-entered query.
4033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * {@link #restoreUserQuery()} will restore the query to the last
4043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * user-entered query.
4053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setQuery(String query, boolean selectAll) {
4073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = false;
4083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView.setText(query);
4093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setTextSelection(selectAll);
4103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = true;
4113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text selection in the query text view.
4153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
4163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param selectAll If {@code true}, selects the entire query.
4173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        If {@false}, no characters are selected, and the cursor is placed
4183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        at the end of the query.
4193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setTextSelection(boolean selectAll) {
4213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (selectAll) {
4225691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            mQueryTextView.selectAll();
4233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
4243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(mQueryTextView.length());
4253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
42818959800eddb7127e4fd9b24e91458d0584cc257Mathew Inwood    protected void updateUi(boolean queryEmpty) {
4295691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateQueryTextView(queryEmpty);
4305691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateSearchGoButton(queryEmpty);
4315691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateVoiceSearchButton(queryEmpty);
4325691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    }
4335691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert
434e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateQueryTextView(boolean queryEmpty) {
435e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
4365691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            if (mCorpus == null || mCorpus.isWebCorpus()) {
4375691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty_google);
4385691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(null);
4395691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            } else {
4405691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty);
4415691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(mCorpus.getHint());
4425691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            }
443e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
444e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setBackgroundResource(R.drawable.textfield_search);
445e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
446e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
447e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
448e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateSearchGoButton(boolean queryEmpty) {
449e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
450e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.GONE);
451e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
452e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.VISIBLE);
453e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
454e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
455e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
456e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected void updateVoiceSearchButton(boolean queryEmpty) {
45781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (queryEmpty && mVoiceSearch.shouldShowVoiceSearch(mCorpus)) {
458e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.VISIBLE);
459e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
460e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
461e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.GONE);
462e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(null);
463e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
464e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
465e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
46641fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert    protected void showCorpusSelectionDialog() {
467f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog == null) {
468f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog = new CorpusSelectionDialog(this);
469f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOwnerActivity(this);
470dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            mCorpusSelectionDialog.setOnDismissListener(new CorpusSelectorDismissListener());
471f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOnCorpusSelectedListener(new CorpusSelectionListener());
4722617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
473f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mCorpusSelectionDialog.show(mCorpus);
4742617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4752617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
476dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    protected boolean isCorpusSelectionDialogShowing() {
477dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        return mCorpusSelectionDialog != null && mCorpusSelectionDialog.isShowing();
478dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
479dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
480f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    protected void dismissCorpusSelectionDialog() {
481f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog != null) {
482f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.dismiss();
4832617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
4842617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4852617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
486dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    /**
487dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     * @return true if a search was performed as a result of this click, false otherwise.
488dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     */
489dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    protected boolean onSearchClicked(int method) {
4903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getQuery();
4913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
492eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
493eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        // Don't do empty queries
494dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (TextUtils.getTrimmedLength(query) == 0) return false;
495eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
49681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
497dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (searchCorpus == null) return false;
498c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
499ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
500c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
501c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log search start
502fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSearch(mCorpus, method, query.length());
503c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
504c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
505c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        SuggestionData searchShortcut = searchCorpus.createSearchShortcut(query);
506c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchShortcut != null) {
507c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            DataSuggestionCursor cursor = new DataSuggestionCursor(query);
508c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            cursor.add(searchShortcut);
509c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            getShortcutRepository().reportClick(cursor, 0);
510c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        }
511c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
512c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start search
513c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createSearchIntent(query, mAppSearchData);
51481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
515dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        return true;
5163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
5193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
52081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
521c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchCorpus == null) return;
522c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
523535931433926d342c6277034cad91143ae28b72dBjorn Bringert        mTookAction = true;
524535931433926d342c6277034cad91143ae28b72dBjorn Bringert
525c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log voice search start
526c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        getLogger().logVoiceSearch(searchCorpus);
527ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
528c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start voice search
529c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createVoiceSearchIntent(mAppSearchData);
53081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
53181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
53281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
53381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    /**
53481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     * Gets the corpus to use for any searches. This is the web corpus in "All" mode,
53581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     * and the selected corpus otherwise.
53681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     */
53781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    private Corpus getSearchCorpus() {
53881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (mCorpus != null) {
53981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return mCorpus;
54081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } else {
54181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Corpus webCorpus = getCorpora().getWebCorpus();
54281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            if (webCorpus == null) {
54381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert                Log.e(TAG, "No web corpus");
54481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            }
54581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return webCorpus;
54681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
547c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert    }
548c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
549e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    protected SuggestionCursor getCurrentSuggestions() {
550fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSuggestionsAdapter.getCurrentSuggestions();
551782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
552782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
55381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    protected void launchIntent(Intent intent) {
55481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (intent == null) {
55581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return;
55681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
55781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        try {
55881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            startActivity(intent);
55981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } catch (RuntimeException ex) {
56081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // Since the intents for suggestions specified by suggestion providers,
56181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // guard against them not being handled, not allowed, etc.
56281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Log.e(TAG, "Failed to start " + intent.toUri(0), ex);
56381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
56481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
56581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
566fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean launchSuggestion(int position) {
5676f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert        SuggestionCursor suggestions = getCurrentSuggestions();
5686f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert        if (position < 0 || position >= suggestions.getCount()) {
5696f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert            Log.w(TAG, "Tried to launch invalid suggestion " + position);
5706f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert            return false;
5716f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert        }
5726f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert
573fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Launching suggestion " + position);
574ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
575e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert
576c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log suggestion click
577e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert        Collection<Corpus> corpora = mSuggestionsAdapter.getSuggestions().getIncludedCorpora();
578fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSuggestionClick(position, suggestions, corpora);
579ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
580c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
581fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getShortcutRepository().reportClick(suggestions, position);
582c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
583c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Launch intent
58481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        suggestions.moveTo(position);
58581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Intent intent = suggestions.getSuggestionIntent(mAppSearchData);
58681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
587c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
5883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
5893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
591fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionLongClicked(int position) {
592fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Long clicked on suggestion " + position);
5933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return false;
5943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
596fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionKeyDown(int position, int keyCode, KeyEvent event) {
597782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Treat enter or search as a click
598782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
599fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return launchSuggestion(position);
600782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
601782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
6026f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_UP && position == 0) {
6037b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved up from the top suggestion, restore the user query and focus query box
6047b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Up and out");
6057b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            restoreUserQuery();
6067b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return false;  // let the framework handle the move
6077b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6087b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
6097b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
6107b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
6117b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved left / right from a suggestion, keep current query, move
6127b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // focus to query box, and move cursor to far left / right
6137b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Left/right on a suggestion");
6144afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            String query = getQuery();
6154afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            int cursorPos = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : query.length();
6167b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.setSelection(cursorPos);
6177b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.requestFocus();
6184afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            updateSuggestions(query);
6197b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return true;
6207b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6217b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
622782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return false;
623782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
624782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
6253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onSourceSelected() {
6263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "No suggestion selected");
6273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        restoreUserQuery();
6283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
630782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected int getSelectedPosition() {
631782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedPosition();
632782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
633782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
6343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method.
6363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void hideInputMethod() {
6383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
6393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (imm != null) {
6403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            imm.hideSoftInputFromWindow(mQueryTextView.getWindowToken(), 0);
6413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6447b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    protected void showInputMethodForQuery() {
6457b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
6467b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (imm != null) {
647d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            imm.showSoftInput(mQueryTextView, 0);
6487b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6497b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
6507b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
6513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method when the suggestions get focus.
6533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
6553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
6567b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Suggestions focus change, now: " + focused);
6577b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
6587b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The suggestions list got focus, hide the input method
6597b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                hideInputMethod();
6607b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            }
6617b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6627b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
6637b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
6647b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class QueryTextViewFocusListener implements OnFocusChangeListener {
6657b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        public void onFocusChange(View v, boolean focused) {
6667b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Query focus change, now: " + focused);
6677b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
668dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert                // The query box got focus, show the input method
6697b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                showInputMethodForQuery();
6703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
674e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney    private int getMaxSuggestions() {
675e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney        Config config = getConfig();
676e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney        return mCorpus == null
677e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney                ? config.getMaxPromotedSuggestions()
678e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney                : config.getMaxResultsPerSource();
679e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney    }
680e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney
6814ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private void updateSuggestionsBuffered() {
682d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.removeCallbacks(mUpdateSuggestionsTask);
6834ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        long delay = getConfig().getTypingUpdateSuggestionsDelayMillis();
684d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.postDelayed(mUpdateSuggestionsTask, delay);
6854ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
6864ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
6873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void updateSuggestions(String query) {
68811234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        // Log start latency if this is the first suggestions update
68911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (mStarting) {
69011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            mStarting = false;
69111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
69211234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            List<Corpus> rankedCorpora = getCorpusRanker().getRankedCorpora();
69311234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            int latency = mStartLatencyTracker.getLatency();
69411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            getLogger().logStart(latency, source, mCorpus, rankedCorpora);
69511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        }
69611234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert
6973d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        query = ltrim(query);
698e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney        Suggestions suggestions = getSuggestionsProvider().getSuggestions(
69981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert                query, mCorpus, getMaxSuggestions());
700185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
7013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7037be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
7047be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        if (!event.isSystem() && !isDpadKey(keyCode)) {
7057be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
7067be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (mQueryTextView.requestFocus()) {
7077be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return mQueryTextView.dispatchKeyEvent(event);
7087be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            }
7097be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
7107be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        return false;
7117be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
7127be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
7137be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean isDpadKey(int keyCode) {
7147be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        switch (keyCode) {
7157be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_UP:
7167be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_DOWN:
7177be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_LEFT:
7187be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_RIGHT:
7197be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_CENTER:
7207be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return true;
7217be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            default:
7227be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return false;
7237be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
7247be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
7257be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
7263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Filters the suggestions list when the search text changes.
7283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
7303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void afterTextChanged(Editable s) {
731e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            boolean empty = s.length() == 0;
732e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            if (empty != mQueryWasEmpty) {
733e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert                mQueryWasEmpty = empty;
7345691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                updateUi(empty);
735e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            }
7363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mUpdateSuggestions) {
7373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                String query = s == null ? "" : s.toString();
7389ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert                setUserQuery(query);
7394ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert                updateSuggestionsBuffered();
7403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
7413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
7443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
7473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Handles non-text keys in the query text view.
7523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class QueryTextViewKeyListener implements View.OnKeyListener {
7543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public boolean onKey(View view, int keyCode, KeyEvent event) {
7553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Handle IME search action key
7563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
757d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood                // if no action was taken, consume the key event so that the keyboard
758d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood                // remains on screen.
759dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood                return !onSearchClicked(Logger.SEARCH_METHOD_KEYBOARD);
7603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
7613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return false;
7623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
765782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
766782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the search and voice search buttons,
767782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * by refocusing to EditText.
768782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
769782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
770782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
7717be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
772782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
773782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
774782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
775782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
776782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the suggestions list view.
777782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
778713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private class SuggestionsViewKeyListener implements View.OnKeyListener {
779782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
780782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN) {
781fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                int position = getSelectedPosition();
782fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                if (onSuggestionKeyDown(position, keyCode, event)) {
7837be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                        return true;
784782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
785782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
7867be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
787782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
788782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
789782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
790145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert    private class InputMethodCloser implements SuggestionsView.OnScrollListener {
791145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
792145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
793145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                int totalItemCount) {
794145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        }
795145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
796145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        public void onScrollStateChanged(AbsListView view, int scrollState) {
797185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            hideInputMethod();
798185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        }
799185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
800185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
801185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
802fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public void onSuggestionClicked(int position) {
803fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           launchSuggestion(position);
8043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
8053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
806fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public boolean onSuggestionLongClicked(int position) {
807fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           return SearchActivity.this.onSuggestionLongClicked(position);
808713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       }
809145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
810145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert       public void onSuggestionQueryRefineClicked(int position) {
811145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert           if (DBG) Log.d(TAG, "query refine clicked, pos " + position);
812145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert           SuggestionCursor suggestions = getCurrentSuggestions();
813145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert           if (suggestions != null) {
814145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert               suggestions.moveTo(position);
815145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert               String query = suggestions.getSuggestionQuery();
816145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert               if (!TextUtils.isEmpty(query)) {
817145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                   query += " ";
818145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                   setUserQuery(query);
819145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                   setQuery(query, false);
820145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                   updateSuggestions(query);
821145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert               }
822145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert           }
823145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert       }
8243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
8267b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class SelectionHandler implements SuggestionSelectionListener {
827fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onSuggestionSelected(int position) {
828e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert            SuggestionCursor suggestions = getCurrentSuggestions();
829fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            suggestions.moveTo(position);
830fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            String displayQuery = suggestions.getSuggestionDisplayQuery();
831fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            if (TextUtils.isEmpty(displayQuery)) {
832fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                restoreUserQuery();
833fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            } else {
834fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                setQuery(displayQuery, false);
835fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            }
836fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
837fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
838fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onNothingSelected() {
839fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // This happens when a suggestion has been selected with the
840fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // dpad / trackball and then a different UI element is touched.
841fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // Do nothing, since we want to keep the query of the selection
842fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // in the search box.
8437b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
8447b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
8457b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
8463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
8472617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the source selector.
8483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
8493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
8503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
851ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            onSearchClicked(Logger.SEARCH_METHOD_BUTTON);
8523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
8533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
8553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
8562617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the search button.
8572617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     */
858fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private class CorpusIndicatorClickListener implements View.OnClickListener {
8592617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        public void onClick(View view) {
86041fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
8612617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
862f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    }
863f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
864dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    private class CorpusSelectorDismissListener implements DialogInterface.OnDismissListener {
865dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        public void onDismiss(DialogInterface dialog) {
866dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            if (DBG) Log.d(TAG, "Corpus selector dismissed");
867dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            clearStartedIntoCorpusSelectionDialog();
868dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
869dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
870dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
871f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private class CorpusSelectionListener
872f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            implements CorpusSelectionDialog.OnCorpusSelectedListener {
87321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        public void onCorpusSelected(String corpusName) {
87421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            setCorpus(corpusName);
875f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            updateSuggestions(getQuery());
876f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mQueryTextView.requestFocus();
877dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            showInputMethodForQuery();
878f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
8792617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
8802617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
8812617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    /**
8823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the voice search button.
8833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
8843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class VoiceSearchButtonClickListener implements View.OnClickListener {
8853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
8863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onVoiceSearchClicked();
8873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
8883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
89021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private class CorporaObserver extends DataSetObserver {
89121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        @Override
89221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        public void onChanged() {
89321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            setCorpus(getCorpusName());
89421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            updateSuggestions(getQuery());
89521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        }
89621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
89721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
8983d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    private static String ltrim(String text) {
8993d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int start = 0;
9003d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int length = text.length();
9013d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        while (start < length && Character.isWhitespace(text.charAt(start))) {
9023d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney            start++;
9033d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        }
9043d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        return start > 0 ? text.substring(start, length) : text;
9053d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    }
9063d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney
9073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
908