SearchActivity.java revision 4ef1338a23b040df2ef180c48ff85e14a9d70906
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;
24883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsFooter;
25185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsView;
26185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
30d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinneyimport android.graphics.drawable.Drawable;
31fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
334ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringertimport android.os.Handler;
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.Editable;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextWatcher;
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.KeyEvent;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
41883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringertimport android.view.ViewGroup;
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View.OnFocusChangeListener;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.EditText;
453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.ImageButton;
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
47e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringertimport java.util.Collection;
4847d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringertimport java.util.Collections;
4947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringertimport java.util.List;
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
65c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // The string used for privateImeOptions to identify to the IME that it should not show
66c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // a microphone button since one already exists in the search dialog.
67c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // TODO: This should move to android-common or something.
68c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    private static final String IME_OPTION_NO_MICROPHONE = "nm";
69c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert
709ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
71fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String INSTANCE_KEY_CORPUS = "corpus";
729ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private static final String INSTANCE_KEY_USER_QUERY = "query";
73713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
74f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    // Measures time from for last onCreate()/onNewIntent() call.
75f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private LatencyTracker mStartLatencyTracker;
76ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
77ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
78ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
79ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
80ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
81ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
82f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private CorpusSelectionDialog mCorpusSelectionDialog;
83f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
84185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    protected SuggestionsAdapter mSuggestionsAdapter;
85185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected EditText mQueryTextView;
87e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    // True if the query was empty on the previous call to updateQuery()
88e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected boolean mQueryWasEmpty = true;
893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected SuggestionsView mSuggestionsView;
91883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert    protected SuggestionsFooter mSuggestionsFooter;
923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mSearchGoButton;
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mVoiceSearchButton;
95a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert    protected ImageButton mCorpusIndicator;
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private Launcher mLauncher;
983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
99fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus mCorpus;
1002617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
101713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mUpdateSuggestions;
102713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private String mUserQuery;
103713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mSelectAll;
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1054ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Handler mUpdateSuggestionsHandler = new Handler();
1064ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Runnable mUpdateSuggestionsTask = new Runnable() {
1074ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        public void run() {
1084ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert            updateSuggestions(getQuery());
1094ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        }
1104ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    };
1114ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
1123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
1133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
115ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
118713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
119e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        setContentView(R.layout.search_activity);
1203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
121816804b67619af133860a1e28e92f58bc642260dBjorn Bringert        mSuggestionsAdapter = getQsbApplication().createSuggestionsAdapter();
122185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView = (EditText) findViewById(R.id.search_src_text);
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
125782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setSuggestionClickListener(new ClickHandler());
1267b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mSuggestionsView.setSuggestionSelectionListener(new SelectionHandler());
127782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setInteractionListener(new InputMethodCloser());
128713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
1297b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mSuggestionsView.setOnFocusChangeListener(new SuggestListFocusListener());
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
131883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert        mSuggestionsFooter = getQsbApplication().createSuggestionsFooter();
132dbdffd8316e75bc2813dbbcbef13d357970e8c84Bjorn Bringert        ViewGroup footerFrame = (ViewGroup) findViewById(R.id.footer);
133dbdffd8316e75bc2813dbbcbef13d357970e8c84Bjorn Bringert        mSuggestionsFooter.addToContainer(footerFrame);
134883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
137a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator = (ImageButton) findViewById(R.id.corpus_indicator);
1383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1399ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mLauncher = new Launcher(this);
1403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
141782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
142782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
1437b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
144782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
145fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnClickListener(new CorpusIndicatorClickListener());
1462617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
147782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
148782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
149782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
1507be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1517be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        ButtonsKeyListener buttonsKeyListener = new ButtonsKeyListener();
1527be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mSearchGoButton.setOnKeyListener(buttonsKeyListener);
1537be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mVoiceSearchButton.setOnKeyListener(buttonsKeyListener);
154fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnKeyListener(buttonsKeyListener);
155713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1569ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUpdateSuggestions = true;
1579ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1589ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1599ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1609ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1619ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1639ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
164713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
165713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
166713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setAdapter(mSuggestionsAdapter);
167dbdffd8316e75bc2813dbbcbef13d357970e8c84Bjorn Bringert        mSuggestionsFooter.setAdapter(mSuggestionsAdapter);
1689ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
169713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1709ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1719ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
172ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1739ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1749ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1759ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1769ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
177ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
178f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mStartLatencyTracker = new LatencyTracker();
179ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
180ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
181ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
182ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
1839ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
1849ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
185fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS);
1869ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
187fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        setCorpus(getCorpus(corpusName));
1889ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setUserQuery(query);
189713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
190713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
191713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
1929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
1939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
1949ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
1959ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
196fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
197fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = mCorpus == null ? null : mCorpus.getName();
198fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        outState.putString(INSTANCE_KEY_CORPUS, corpusName);
1999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        outState.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
200713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
201713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
202713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
203713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
204fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpusFromUri(intent.getData());
2052617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
2062617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
2072617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
208fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        setCorpus(corpus);
2092617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        setUserQuery(query);
210154017089860da89ec020ed37e502d194253cc67Bjorn Bringert        mSelectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
211c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mAppSearchData = appSearchData;
2122617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
213fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(intent.getAction())) {
21441fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
2152617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
2169ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
2179ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
218fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static Uri getCorpusUri(Corpus corpus) {
219fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) return null;
220fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return new Uri.Builder()
221fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .scheme(SCHEME_CORPUS)
222fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .authority(corpus.getName())
223fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .build();
224fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
225fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
226fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpusFromUri(Uri uri) {
227fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
228fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
229fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String name = uri.getAuthority();
230fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getCorpus(name);
231fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
232fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
233fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpus(String sourceName) {
2340ccab5903ba2c64ec4cdd33dbcea91b1e279d556Bjorn Bringert        if (sourceName == null) return null;
235fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpora().getCorpus(sourceName);
236fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
237fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            Log.w(TAG, "Unknown corpus " + sourceName);
2389ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
239713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
240fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return corpus;
241713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
242713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
243fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private void setCorpus(Corpus corpus) {
244fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "setCorpus(" + corpus + ")");
245fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpus = corpus;
2469ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Drawable sourceIcon;
247fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
248c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert            sourceIcon = getCorpusViewFactory().getGlobalSearchIcon();
249713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        } else {
250fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            sourceIcon = corpus.getCorpusIcon();
251713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
252fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mSuggestionsAdapter.setCorpus(corpus);
253a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator.setImageDrawable(sourceIcon);
2543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2555691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateUi(getQuery().length() == 0);
2560484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
2570484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
2583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (QsbApplication) getApplication();
2603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2624ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Config getConfig() {
2634ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        return getQsbApplication().getConfig();
2644ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
2654ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
266fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpora getCorpora() {
267fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getCorpora();
268fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
269fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
2703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private ShortcutRepository getShortcutRepository() {
2713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getShortcutRepository();
2723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
274ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
27547d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        return getQsbApplication().getSuggestionsProvider();
27647d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
27747d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
27847d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    private CorpusRanker getCorpusRanker() {
27947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        return getQsbApplication().getCorpusRanker();
2803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
282c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert    private CorpusViewFactory getCorpusViewFactory() {
283c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert        return getQsbApplication().getCorpusViewFactory();
284185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
285185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
286ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
287ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
288ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
289ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
2903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
2923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
2933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onDestroy();
294dbdffd8316e75bc2813dbbcbef13d357970e8c84Bjorn Bringert        mSuggestionsFooter.setAdapter(null);
295185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsView.setAdapter(null);  // closes mSuggestionsAdapter
2963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
3009ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
301f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (!mTookAction) {
302f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // TODO: This gets logged when starting other activities, e.g. by opening he search
303f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // settings, or clicking a notification in the status bar.
304f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logExit(getCurrentSuggestions(), getQuery().length());
305f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
3069ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
3073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
308185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
30994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        getQsbApplication().getShortcutRefresher().reset();
310f69873ae02e6d8f8a550f5fa43b5b1f2cbea96ffBjorn Bringert        dismissCorpusSelectionDialog();
3113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
3123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
315b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
3169ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
317b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
3189ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setQuery(mUserQuery, mSelectAll);
3199ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Only select everything the first time after creating the activity.
3209ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSelectAll = false;
3219ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        updateSuggestions(mUserQuery);
322b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        mQueryTextView.requestFocus();
323ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        if (mStarting) {
324ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            mStarting = false;
3253f71a11f8858b9164ca83e8d2f558dd8a8a5e2deBjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
326f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            List<Corpus> rankedCorpora = getCorpusRanker().getRankedCorpora();
327f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            int latency = mStartLatencyTracker.getLatency();
328f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logStart(latency, source, mCorpus, rankedCorpora);
329ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        }
330b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
331b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
332b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
3333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean onCreateOptionsMenu(Menu menu) {
3343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreateOptionsMenu(menu);
33526d85260316193a1aeff5d407d4f32f5297be012Bjorn Bringert        SearchSettings.addSearchSettingsMenuItem(this, menu);
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3399ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    /**
3409ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * Sets the query as typed by the user. Does not update the suggestions
3419ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * or the text in the query box.
3429ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     */
3439ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setUserQuery(String userQuery) {
3449ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (userQuery == null) userQuery = "";
3459ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUserQuery = userQuery;
3469ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
3479ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
348713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        CharSequence q = mQueryTextView.getText();
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return q == null ? "" : q.toString();
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Restores the query entered by the user.
3553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void restoreUserQuery() {
3573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Restoring query to '" + mUserQuery + "'");
3583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setQuery(mUserQuery, false);
3593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions,
3633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * and does not change the saved user-entered query.
3643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * {@link #restoreUserQuery()} will restore the query to the last
3653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * user-entered query.
3663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setQuery(String query, boolean selectAll) {
3683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = false;
3693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView.setText(query);
3703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setTextSelection(selectAll);
3713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = true;
3723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text selection in the query text view.
3763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param selectAll If {@code true}, selects the entire query.
3783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        If {@false}, no characters are selected, and the cursor is placed
3793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        at the end of the query.
3803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setTextSelection(boolean selectAll) {
3823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (selectAll) {
3835691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            mQueryTextView.selectAll();
3843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
3853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(mQueryTextView.length());
3863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3895691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    private void updateUi(boolean queryEmpty) {
3905691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateQueryTextView(queryEmpty);
3915691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateSearchGoButton(queryEmpty);
3925691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateVoiceSearchButton(queryEmpty);
3935691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    }
3945691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert
395e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateQueryTextView(boolean queryEmpty) {
396e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
3975691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            if (mCorpus == null || mCorpus.isWebCorpus()) {
3985691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty_google);
3995691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(null);
4005691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            } else {
4015691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty);
4025691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(mCorpus.getHint());
4035691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            }
404e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
405e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setBackgroundResource(R.drawable.textfield_search);
406e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
407e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
408e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
409e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateSearchGoButton(boolean queryEmpty) {
410e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
411e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.GONE);
412e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
413e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.VISIBLE);
414e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
415e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
416e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
417e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected void updateVoiceSearchButton(boolean queryEmpty) {
418c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (queryEmpty && mLauncher.shouldShowVoiceSearch(mCorpus)) {
419e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.VISIBLE);
420e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
421e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
422e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.GONE);
423e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(null);
424e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
425e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
426e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
42741fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert    protected void showCorpusSelectionDialog() {
428f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog == null) {
429f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog = new CorpusSelectionDialog(this);
430f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOwnerActivity(this);
431f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOnCorpusSelectedListener(new CorpusSelectionListener());
4322617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
433f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mCorpusSelectionDialog.show(mCorpus);
4342617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4352617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
436f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    protected void dismissCorpusSelectionDialog() {
437f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog != null) {
438f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.dismiss();
4392617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
4402617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4412617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
442ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    protected void onSearchClicked(int method) {
4433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getQuery();
4443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
445eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
446eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        // Don't do empty queries
447eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        if (TextUtils.getTrimmedLength(query) == 0) return;
448eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
449535931433926d342c6277034cad91143ae28b72dBjorn Bringert        Corpus searchCorpus = mLauncher.getSearchCorpus(getCorpora(), mCorpus);
450c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchCorpus == null) return;
451c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
452ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
453c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
454c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log search start
455fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSearch(mCorpus, method, query.length());
456c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
457c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
458c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        SuggestionData searchShortcut = searchCorpus.createSearchShortcut(query);
459c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchShortcut != null) {
460c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            DataSuggestionCursor cursor = new DataSuggestionCursor(query);
461c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            cursor.add(searchShortcut);
462c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            getShortcutRepository().reportClick(cursor, 0);
463c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        }
464c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
465c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start search
466c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createSearchIntent(query, mAppSearchData);
467c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mLauncher.launchIntent(intent);
4683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
4713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
472535931433926d342c6277034cad91143ae28b72dBjorn Bringert        Corpus searchCorpus = mLauncher.getSearchCorpus(getCorpora(), mCorpus);
473c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchCorpus == null) return;
474c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
475535931433926d342c6277034cad91143ae28b72dBjorn Bringert        mTookAction = true;
476535931433926d342c6277034cad91143ae28b72dBjorn Bringert
477c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log voice search start
478c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        getLogger().logVoiceSearch(searchCorpus);
479ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
480c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start voice search
481c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createVoiceSearchIntent(mAppSearchData);
482c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mLauncher.launchIntent(intent);
483c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert    }
484c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
485e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    protected SuggestionCursor getCurrentSuggestions() {
486fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSuggestionsAdapter.getCurrentSuggestions();
487782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
488782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
489fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean launchSuggestion(int position) {
490fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Launching suggestion " + position);
491ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
492e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert
493e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert        SuggestionCursor suggestions = getCurrentSuggestions();
494c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
495c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log suggestion click
496e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert        Collection<Corpus> corpora = mSuggestionsAdapter.getSuggestions().getIncludedCorpora();
497fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSuggestionClick(position, suggestions, corpora);
498ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
499c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
500fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getShortcutRepository().reportClick(suggestions, position);
501c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
502c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Launch intent
503c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = mLauncher.getSuggestionIntent(suggestions, position, mAppSearchData);
504c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mLauncher.launchIntent(intent);
505c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
5063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
5073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
509fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionLongClicked(int position) {
510fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Long clicked on suggestion " + position);
5113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return false;
5123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
514fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionKeyDown(int position, int keyCode, KeyEvent event) {
515782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Treat enter or search as a click
516782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
517fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return launchSuggestion(position);
518782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
519782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
5207b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_UP
5217b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                && mSuggestionsView.getSelectedItemPosition() == 0) {
5227b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved up from the top suggestion, restore the user query and focus query box
5237b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Up and out");
5247b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            restoreUserQuery();
5257b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return false;  // let the framework handle the move
5267b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5277b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
5287b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
5297b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
5307b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // Moved left / right from a suggestion, keep current query, move
5317b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            // focus to query box, and move cursor to far left / right
5327b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Left/right on a suggestion");
5334afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            String query = getQuery();
5344afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            int cursorPos = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : query.length();
5357b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.setSelection(cursorPos);
5367b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            mQueryTextView.requestFocus();
5374afd6f1587b1c2f3686190d55f1640ecfba03699Bjorn Bringert            updateSuggestions(query);
5387b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            return true;
5397b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5407b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
541782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return false;
542782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
543782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
5443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onSourceSelected() {
5453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "No suggestion selected");
5463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        restoreUserQuery();
5473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
549782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected int getSelectedPosition() {
550782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedPosition();
551782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
552782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
5533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method.
5553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void hideInputMethod() {
5573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
5583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (imm != null) {
5593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            imm.hideSoftInputFromWindow(mQueryTextView.getWindowToken(), 0);
5603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5637b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    protected void showInputMethodForQuery() {
5647b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
5657b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        if (imm != null) {
5666c8a8cb66ccf57347c7aed4260132d342b242b0fBjorn Bringert            imm.showSoftInput(mQueryTextView, InputMethodManager.SHOW_IMPLICIT);
5677b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5687b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
5697b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
5703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method when the suggestions get focus.
5723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
5743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
5757b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Suggestions focus change, now: " + focused);
5767b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
5777b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The suggestions list got focus, hide the input method
5787b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                hideInputMethod();
5797b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            }
5807b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
5817b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
5827b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
5837b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class QueryTextViewFocusListener implements OnFocusChangeListener {
5847b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        public void onFocusChange(View v, boolean focused) {
5857b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Query focus change, now: " + focused);
5867b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
5877b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The query box got focus, show the input method if the
5887b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // query box got focus?
5897b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                showInputMethodForQuery();
5903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
5913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
59447d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    private List<Corpus> getCorporaToQuery() {
59547d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        if (mCorpus == null) {
59647d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert            return getCorpusRanker().getRankedCorpora();
59747d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        } else {
59847d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert            return Collections.singletonList(mCorpus);
59947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        }
60047d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
60147d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
6024ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private void updateSuggestionsBuffered() {
6034ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        mUpdateSuggestionsHandler.removeCallbacks(mUpdateSuggestionsTask);
6044ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        long delay = getConfig().getTypingUpdateSuggestionsDelayMillis();
6054ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        mUpdateSuggestionsHandler.postDelayed(mUpdateSuggestionsTask, delay);
6064ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
6074ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
6083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void updateSuggestions(String query) {
6093d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        query = ltrim(query);
61047d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        List<Corpus> corporaToQuery = getCorporaToQuery();
61147d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        Suggestions suggestions = getSuggestionsProvider().getSuggestions(query, corporaToQuery);
612185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
6133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6157be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
6167be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        if (!event.isSystem() && !isDpadKey(keyCode)) {
6177be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
6187be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (mQueryTextView.requestFocus()) {
6197be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return mQueryTextView.dispatchKeyEvent(event);
6207be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            }
6217be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
6227be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        return false;
6237be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
6247be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
6257be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean isDpadKey(int keyCode) {
6267be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        switch (keyCode) {
6277be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_UP:
6287be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_DOWN:
6297be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_LEFT:
6307be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_RIGHT:
6317be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_CENTER:
6327be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return true;
6337be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            default:
6347be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return false;
6357be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
6367be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
6377be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
6383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Filters the suggestions list when the search text changes.
6403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
6423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void afterTextChanged(Editable s) {
643e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            boolean empty = s.length() == 0;
644e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            if (empty != mQueryWasEmpty) {
645e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert                mQueryWasEmpty = empty;
6465691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                updateUi(empty);
647e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            }
6483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mUpdateSuggestions) {
6493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                String query = s == null ? "" : s.toString();
6509ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert                setUserQuery(query);
6514ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert                updateSuggestionsBuffered();
6523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
6563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
6593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Handles non-text keys in the query text view.
6643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class QueryTextViewKeyListener implements View.OnKeyListener {
6663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public boolean onKey(View view, int keyCode, KeyEvent event) {
6673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Handle IME search action key
6683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
669ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert                onSearchClicked(Logger.SEARCH_METHOD_KEYBOARD);
6703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return false;
6723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
675782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
676782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the search and voice search buttons,
677782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * by refocusing to EditText.
678782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
679782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
680782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
6817be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
682782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
683782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
684782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
685782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
686782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the suggestions list view.
687782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
688713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private class SuggestionsViewKeyListener implements View.OnKeyListener {
689782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
690782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN) {
691fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                int position = getSelectedPosition();
692fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                if (onSuggestionKeyDown(position, keyCode, event)) {
6937be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                        return true;
694782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
695782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
6967be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
697782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
698782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
699782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
700185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class InputMethodCloser implements SuggestionsView.InteractionListener {
701185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        public void onInteraction() {
702185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            hideInputMethod();
703185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        }
704185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
705185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
706185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
707fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public void onSuggestionClicked(int position) {
708fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           launchSuggestion(position);
7093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
7103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
711fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public boolean onSuggestionLongClicked(int position) {
712fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           return SearchActivity.this.onSuggestionLongClicked(position);
713713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       }
7143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7167b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class SelectionHandler implements SuggestionSelectionListener {
717fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onSuggestionSelected(int position) {
718e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert            SuggestionCursor suggestions = getCurrentSuggestions();
719fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            suggestions.moveTo(position);
720fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            String displayQuery = suggestions.getSuggestionDisplayQuery();
721fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            if (TextUtils.isEmpty(displayQuery)) {
722fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                restoreUserQuery();
723fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            } else {
724fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                setQuery(displayQuery, false);
725fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            }
726fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
727fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
728fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public void onNothingSelected() {
729fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // This happens when a suggestion has been selected with the
730fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // dpad / trackball and then a different UI element is touched.
731fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // Do nothing, since we want to keep the query of the selection
732fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                // in the search box.
7337b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
7347b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
7357b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
7363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7372617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the source selector.
7383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
7403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
741ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            onSearchClicked(Logger.SEARCH_METHOD_BUTTON);
7423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7462617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the search button.
7472617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     */
748fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private class CorpusIndicatorClickListener implements View.OnClickListener {
7492617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        public void onClick(View view) {
75041fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
7512617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
752f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    }
753f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
754f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private class CorpusSelectionListener
755f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            implements CorpusSelectionDialog.OnCorpusSelectedListener {
756f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        public void onCorpusSelected(Corpus corpus) {
757f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            setCorpus(corpus);
758f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            updateSuggestions(getQuery());
759f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mQueryTextView.requestFocus();
760f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
7612617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
7622617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
7632617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    /**
7643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the voice search button.
7653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class VoiceSearchButtonClickListener implements View.OnClickListener {
7673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
7683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onVoiceSearchClicked();
7693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7723d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    private static String ltrim(String text) {
7733d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int start = 0;
7743d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        int length = text.length();
7753d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        while (start < length && Character.isWhitespace(text.charAt(start))) {
7763d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney            start++;
7773d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        }
7783d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney        return start > 0 ? text.substring(start, length) : text;
7793d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney    }
7803d382f0ca49a8150d504cf2629124a7f13175e1eBryan Mawhinney
7813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
782