SearchActivity.java revision fde948e69f59589cf0d217ea414af7947de600bb
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;
20fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport com.android.quicksearchbox.ui.CorpusIndicator;
21185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionClickListener;
227b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionSelectionListener;
23185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionViewFactory;
24185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsAdapter;
25185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsView;
26185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
282617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringertimport android.app.Dialog;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.DataSetObserver;
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.drawable.Animatable;
33d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinneyimport android.graphics.drawable.Drawable;
34fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
36ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringertimport android.os.SystemClock;
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.Editable;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextWatcher;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.KeyEvent;
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View.OnFocusChangeListener;
453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.EditText;
473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.ImageButton;
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
49ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringertimport java.util.ArrayList;
50ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final boolean DBG = true;
580484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchActivity";
593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
60fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String SCHEME_CORPUS = "qsb.corpus";
61fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
62fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static final String INTENT_ACTION_QSB_AND_SELECT_CORPUS
63fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            = "com.android.quicksearchbox.action.QSB_AND_SELECT_CORPUS";
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
66fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String INSTANCE_KEY_CORPUS = "corpus";
679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private static final String INSTANCE_KEY_USER_QUERY = "query";
68713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
692617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    // Dialog IDs
70fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final int CORPUS_SELECTION_DIALOG = 0;
712617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
72ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Timestamp for last onCreate()/onNewIntent() call, as returned by SystemClock.uptimeMillis().
73ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private long mStartTime;
74ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
75ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
76ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
77ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
78ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
79ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
80185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    protected SuggestionsAdapter mSuggestionsAdapter;
81185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected EditText mQueryTextView;
833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected SuggestionsView mSuggestionsView;
853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mSearchGoButton;
873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mVoiceSearchButton;
88fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected CorpusIndicator mCorpusIndicator;
893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private Launcher mLauncher;
913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
92fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus mCorpus;
932617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
94713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mUpdateSuggestions;
95713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private String mUserQuery;
96713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mSelectAll;
973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
101ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
104713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setContentView(R.layout.search_bar);
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
107816804b67619af133860a1e28e92f58bc642260dBjorn Bringert        mSuggestionsAdapter = getQsbApplication().createSuggestionsAdapter();
108185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
1093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView = (EditText) findViewById(R.id.search_src_text);
1103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
111782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setSuggestionClickListener(new ClickHandler());
1127b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mSuggestionsView.setSuggestionSelectionListener(new SelectionHandler());
113782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setInteractionListener(new InputMethodCloser());
114713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
1157b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mSuggestionsView.setOnFocusChangeListener(new SuggestListFocusListener());
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
119fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator = new CorpusIndicator(findViewById(R.id.corpus_indicator));
1203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1219ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mLauncher = new Launcher(this);
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
123782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
124782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
1257b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
126782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
127fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnClickListener(new CorpusIndicatorClickListener());
1282617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
129782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
130782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
131782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
1327be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1337be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        ButtonsKeyListener buttonsKeyListener = new ButtonsKeyListener();
1347be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mSearchGoButton.setOnKeyListener(buttonsKeyListener);
1357be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mVoiceSearchButton.setOnKeyListener(buttonsKeyListener);
136fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnKeyListener(buttonsKeyListener);
137713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1389ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUpdateSuggestions = true;
1399ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1409ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1419ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1429ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1439ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1449ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1459ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
146713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
147713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
148713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setAdapter(mSuggestionsAdapter);
1499ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
150713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1519ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1529ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
153ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1549ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1559ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1569ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1579ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
158ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
159ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStartTime = SystemClock.uptimeMillis();
160ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
161ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
162ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
163ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
1649ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
1659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
166fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS);
1679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
168fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        setCorpus(getCorpus(corpusName));
1699ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setUserQuery(query);
170713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
171713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
172713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
1739ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
1749ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
1759ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
1769ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
177fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
178fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = mCorpus == null ? null : mCorpus.getName();
179fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        outState.putString(INSTANCE_KEY_CORPUS, corpusName);
1809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        outState.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
181713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
182713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
183713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
184713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
185fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpusFromUri(intent.getData());
1862617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
1872617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
1882617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
189fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        setCorpus(corpus);
1902617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        setUserQuery(query);
191154017089860da89ec020ed37e502d194253cc67Bjorn Bringert        mSelectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
1922617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        setAppSearchData(appSearchData);
1932617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
194fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(intent.getAction())) {
1952617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert            showSourceSelectorDialog();
1962617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
1979ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1989ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
199fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static Uri getCorpusUri(Corpus corpus) {
200fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) return null;
201fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return new Uri.Builder()
202fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .scheme(SCHEME_CORPUS)
203fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .authority(corpus.getName())
204fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .build();
205fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
206fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
207fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpusFromUri(Uri uri) {
208fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
209fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
210fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String name = uri.getAuthority();
211fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getCorpus(name);
212fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
213fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
214fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpus(String sourceName) {
2150ccab5903ba2c64ec4cdd33dbcea91b1e279d556Bjorn Bringert        if (sourceName == null) return null;
216fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpora().getCorpus(sourceName);
217fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
218fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            Log.w(TAG, "Unknown corpus " + sourceName);
2199ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
220713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
221fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return corpus;
222713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
223713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
224fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private void setCorpus(Corpus corpus) {
225fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "setCorpus(" + corpus + ")");
226fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpus = corpus;
2279ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Drawable sourceIcon;
228fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
2299ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            sourceIcon = getSuggestionViewFactory().getGlobalSearchIcon();
230713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        } else {
231fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            sourceIcon = corpus.getCorpusIcon();
232713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
233fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mSuggestionsAdapter.setCorpus(corpus);
234fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setSourceIcon(sourceIcon);
2353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
236fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        boolean enableVoiceSearch = Launcher.shouldShowVoiceSearch(this, mCorpus);
237fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mVoiceSearchButton.setVisibility(enableVoiceSearch ? View.VISIBLE : View.GONE);
2380484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
2390484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
2403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
2413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (QsbApplication) getApplication();
2423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
244fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpora getCorpora() {
245fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getCorpora();
246fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
247fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
248fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private CorpusRanker getCorpusRanker() {
249fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getCorpusRanker();
250713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
251713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
2523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private ShortcutRepository getShortcutRepository() {
2533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getShortcutRepository();
2543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
256ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
257fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getSuggestionsProvider(mCorpus);
2583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
260185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private SuggestionViewFactory getSuggestionViewFactory() {
261185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        return getQsbApplication().getSuggestionViewFactory();
262185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
263185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
264ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
265ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
266ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
267ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
2683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
2703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
2713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onDestroy();
272185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsView.setAdapter(null);  // closes mSuggestionsAdapter
2733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
2779ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
2789ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
2793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
280185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
28194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        getQsbApplication().getShortcutRefresher().reset();
2823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
2833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
286b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
2879ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
288b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
2899ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setQuery(mUserQuery, mSelectAll);
2909ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Only select everything the first time after creating the activity.
2919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSelectAll = false;
2929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        updateSuggestions(mUserQuery);
293b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        mQueryTextView.requestFocus();
294ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        if (mStarting) {
295ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            mStarting = false;
296ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            // Start up latency should not exceed 2^31 ms (~ 25 days). Note that
297ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            // SystemClock.uptimeMillis() does not advance during deep sleep.
298ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            int latency = (int) (SystemClock.uptimeMillis() - mStartTime);
2993f71a11f8858b9164ca83e8d2f558dd8a8a5e2deBjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
300fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            getLogger().logStart(latency, source, mCorpus,
301fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                    getSuggestionsProvider().getOrderedCorpora());
302ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        }
303b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
304b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
305b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
3063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean onCreateOptionsMenu(Menu menu) {
3073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreateOptionsMenu(menu);
3083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3092617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Intent settings = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS);
3103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        settings.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
3113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // Don't show activity chooser if there are multiple search settings activities,
3123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // e.g. from different QSB implementations.
3133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        settings.setPackage(this.getPackageName());
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        menu.add(Menu.NONE, Menu.NONE, 0, R.string.menu_settings)
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
3163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .setIntent(settings);
3173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
3193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3219ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    /**
3229ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * Sets the query as typed by the user. Does not update the suggestions
3239ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * or the text in the query box.
3249ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     */
3259ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setUserQuery(String userQuery) {
3269ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (userQuery == null) userQuery = "";
3279ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUserQuery = userQuery;
3289ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
3299ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
3309ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setAppSearchData(Bundle appSearchData) {
3312617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        mAppSearchData = appSearchData;
3329ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mLauncher.setAppSearchData(appSearchData);
3339ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
3349ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
335713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        CharSequence q = mQueryTextView.getText();
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return q == null ? "" : q.toString();
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Restores the query entered by the user.
3423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void restoreUserQuery() {
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Restoring query to '" + mUserQuery + "'");
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setQuery(mUserQuery, false);
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions,
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * and does not change the saved user-entered query.
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * {@link #restoreUserQuery()} will restore the query to the last
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * user-entered query.
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setQuery(String query, boolean selectAll) {
3553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = false;
3563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView.setText(query);
3573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setTextSelection(selectAll);
3583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = true;
3593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text selection in the query text view.
3633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param selectAll If {@code true}, selects the entire query.
3653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        If {@false}, no characters are selected, and the cursor is placed
3663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        at the end of the query.
3673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setTextSelection(boolean selectAll) {
3693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (selectAll) {
3703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(0, mQueryTextView.length());
3713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
3723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(mQueryTextView.length());
3733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3762617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    protected void showSourceSelectorDialog() {
377fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        showDialog(CORPUS_SELECTION_DIALOG);
3782617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
3792617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
3802617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
3812617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    @Override
3822617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    protected Dialog onCreateDialog(int id, Bundle args) {
3832617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        switch (id) {
384fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            case CORPUS_SELECTION_DIALOG:
385fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                return createCorpusSelectionDialog();
3862617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert            default:
3872617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert                throw new IllegalArgumentException("Unknown dialog: " + id);
3882617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
3892617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
3902617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
3912617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    @Override
3922617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
3932617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        switch (id) {
394fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            case CORPUS_SELECTION_DIALOG:
395fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                prepareCorpusSelectionDialog((CorpusSelectionDialog) dialog);
3962617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert                break;
3972617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert            default:
3982617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert                throw new IllegalArgumentException("Unknown dialog: " + id);
3992617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
4002617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4012617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
402fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected CorpusSelectionDialog createCorpusSelectionDialog() {
403fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        CorpusSelectionDialog dialog = new CorpusSelectionDialog(this);
4042617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        dialog.setOwnerActivity(this);
4052617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        return dialog;
4062617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4072617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
408fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected void prepareCorpusSelectionDialog(CorpusSelectionDialog dialog) {
409fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        dialog.setCorpus(mCorpus);
4102617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        dialog.setQuery(getQuery());
4112617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        dialog.setAppData(mAppSearchData);
4122617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4132617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
414ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    protected void onSearchClicked(int method) {
4153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getQuery();
4163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
417ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
418fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSearch(mCorpus, method, query.length());
419fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mLauncher.startSearch(mCorpus, query);
4203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
4233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
424ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
425fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logVoiceSearch(mCorpus);
426ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
42784b72b7b9dacd54de9eff630dbbb37de372560f6Bjorn Bringert        // TODO: should this start voice search in the current source?
428fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mLauncher.startVoiceSearch(mCorpus);
4293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
431fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected SuggestionCursor getSuggestions() {
432fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSuggestionsAdapter.getCurrentSuggestions();
433782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
434782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
435fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean launchSuggestion(int position) {
436fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Launching suggestion " + position);
437ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
438fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        SuggestionCursor suggestions = getSuggestions();
439ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        // TODO: This should be just the queried sources, but currently
440ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        // all sources are queried
441fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        ArrayList<Corpus> corpora = getCorpusRanker().rankCorpora(getCorpora().getEnabledCorpora());
442fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSuggestionClick(position, suggestions, corpora);
443ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
444fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mLauncher.launchSuggestion(suggestions, position);
445fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getShortcutRepository().reportClick(suggestions, position);
4463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
4473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
449fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionLongClicked(int position) {
450fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Long clicked on suggestion " + position);
4513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return false;
4523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
454fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionKeyDown(int position, int keyCode, KeyEvent event) {
455782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Treat enter or search as a click
456782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
457fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return launchSuggestion(position);
458782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
459782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4607b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_UP
4617b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                && mSuggestionsView.getSelectedItemPosition() == 0) {
4627b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved up from the top suggestion, restore the user query and focus query box
4637b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Up and out");
4647b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            restoreUserQuery();
4657b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return false;  // let the framework handle the move
4667b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
4677b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
4687b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
4697b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
4707b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved left / right from a suggestion, keep current query, move
4717b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // focus to query box, and move cursor to far left / right
4727b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Left/right on a suggestion");
4737b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            int cursorPos = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView.length();
4747b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.setSelection(cursorPos);
4757b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.requestFocus();
4767b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // TODO: should we modify the list selection?
4777b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return true;
4787b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
4797b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
480782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return false;
481782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
482782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onSourceSelected() {
4843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "No suggestion selected");
4853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        restoreUserQuery();
4863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
488782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected int getSelectedPosition() {
489782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedPosition();
490782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
491782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method.
4943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void hideInputMethod() {
4963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
4973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (imm != null) {
4983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            imm.hideSoftInputFromWindow(mQueryTextView.getWindowToken(), 0);
4993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5027b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    protected void showInputMethodForQuery() {
5037b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
5047b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (imm != null) {
5057b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            imm.showSoftInput(mQueryTextView, 0);
5067b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5077b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
5087b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
5093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method when the suggestions get focus.
5113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
5133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
5147b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Suggestions focus change, now: " + focused);
5157b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
5167b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The suggestions list got focus, hide the input method
5177b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                hideInputMethod();
5187b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            }
5197b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5207b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
5217b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
5227b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class QueryTextViewFocusListener implements OnFocusChangeListener {
5237b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        public void onFocusChange(View v, boolean focused) {
5247b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Query focus change, now: " + focused);
5257b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
5267b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The query box got focus, show the input method if the
5277b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // query box got focus?
5287b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                showInputMethodForQuery();
5293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
5303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void startSearchProgress() {
5343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: Cache animation between calls?
5353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton.setImageResource(R.drawable.searching);
5363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Animatable animation = (Animatable) mSearchGoButton.getDrawable();
5373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        animation.start();
5383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void stopSearchProgress() {
541d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        Drawable animation = mSearchGoButton.getDrawable();
542d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        if (animation instanceof Animatable) {
543d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            // TODO: Is this needed, or is it done automatically when the
544d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            // animation is removed?
545d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            ((Animatable) animation).stop();
546d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        }
5473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton.setImageResource(R.drawable.ic_btn_search);
5483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void updateSuggestions(String query) {
5513d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        query = ltrim(query);
5523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        LatencyTracker latency = new LatencyTracker(TAG);
553ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        Suggestions suggestions = getSuggestionsProvider().getSuggestions(query);
5543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        latency.addEvent("getSuggestions_done");
5553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (!suggestions.isDone()) {
5563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            suggestions.registerDataSetObserver(new ProgressUpdater(suggestions));
5573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            startSearchProgress();
558d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        } else {
559d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            stopSearchProgress();
5603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
561185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
5623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        latency.addEvent("shortcuts_shown");
5633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        long userVisibleLatency = latency.getUserVisibleLatency();
5643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) {
5653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.d(TAG, "User visible latency (shortcuts): " + userVisibleLatency + " ms.");
5663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5697be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
5707be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        if (!event.isSystem() && !isDpadKey(keyCode)) {
5717be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
5727be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (mQueryTextView.requestFocus()) {
5737be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return mQueryTextView.dispatchKeyEvent(event);
5747be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            }
5757be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
5767be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        return false;
5777be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
5787be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
5797be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean isDpadKey(int keyCode) {
5807be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        switch (keyCode) {
5817be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_UP:
5827be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_DOWN:
5837be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_LEFT:
5847be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_RIGHT:
5857be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_CENTER:
5867be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return true;
5877be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            default:
5887be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return false;
5897be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
5907be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
5917be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
5923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Filters the suggestions list when the search text changes.
5943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
5963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void afterTextChanged(Editable s) {
5973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mUpdateSuggestions) {
5983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                String query = s == null ? "" : s.toString();
5999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert                setUserQuery(query);
6003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                updateSuggestions(query);
6013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
6053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
6083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Handles non-text keys in the query text view.
6133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class QueryTextViewKeyListener implements View.OnKeyListener {
6153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public boolean onKey(View view, int keyCode, KeyEvent event) {
6163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Handle IME search action key
6173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
618ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert                onSearchClicked(Logger.SEARCH_METHOD_KEYBOARD);
6193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return false;
6213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
624782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
625782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the search and voice search buttons,
626782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * by refocusing to EditText.
627782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
628782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
629782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
6307be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
631782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
632782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
633782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
634782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
635782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the suggestions list view.
636782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
637713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private class SuggestionsViewKeyListener implements View.OnKeyListener {
638782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
639782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN) {
640fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                int position = getSelectedPosition();
641fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                if (onSuggestionKeyDown(position, keyCode, event)) {
6427be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                        return true;
643782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
644782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
6457be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
646782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
647782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
648782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
649185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class InputMethodCloser implements SuggestionsView.InteractionListener {
650185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        public void onInteraction() {
651185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            hideInputMethod();
652185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        }
653185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
654185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
655185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
656fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public void onSuggestionClicked(int position) {
657fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           launchSuggestion(position);
6583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
6593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
660fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public boolean onSuggestionLongClicked(int position) {
661fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           return SearchActivity.this.onSuggestionLongClicked(position);
662713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       }
6633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6657b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class SelectionHandler implements SuggestionSelectionListener {
666fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onSuggestionSelected(int position) {
667fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            SuggestionCursor suggestions = getSuggestions();
668fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            suggestions.moveTo(position);
669fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            String displayQuery = suggestions.getSuggestionDisplayQuery();
670fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            if (TextUtils.isEmpty(displayQuery)) {
671fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                restoreUserQuery();
672fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            } else {
673fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                setQuery(displayQuery, false);
674fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            }
675fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
676fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
677fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onNothingSelected() {
678fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // This happens when a suggestion has been selected with the
679fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // dpad / trackball and then a different UI element is touched.
680fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // Do nothing, since we want to keep the query of the selection
681fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // in the search box.
6827b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6837b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
6847b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
6853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6862617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the source selector.
6873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
6893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
690ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            onSearchClicked(Logger.SEARCH_METHOD_BUTTON);
6913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6952617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the search button.
6962617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     */
697fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private class CorpusIndicatorClickListener implements View.OnClickListener {
6982617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        public void onClick(View view) {
6992617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert            showSourceSelectorDialog();
7002617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
7012617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
7022617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
7032617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    /**
7043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the voice search button.
7053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class VoiceSearchButtonClickListener implements View.OnClickListener {
7073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
7083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onVoiceSearchClicked();
7093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Updates the progress bar when the suggestions adapter changes its progress.
7143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class ProgressUpdater extends DataSetObserver {
7163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        private final Suggestions mSuggestions;
7173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public ProgressUpdater(Suggestions suggestions) {
7193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mSuggestions = suggestions;
7203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        @Override
7233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onChanged() {
7243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mSuggestions.isDone()) {
7253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                stopSearchProgress();
7263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
7273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
729713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
7303d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    private static String ltrim(String text) {
7313d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int start = 0;
7323d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int length = text.length();
7333d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        while (start < length && Character.isWhitespace(text.charAt(start))) {
7343d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney            start++;
7353d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        }
7363d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        return start > 0 ? text.substring(start, length) : text;
7373d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    }
7383d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney
7393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
740