SearchActivity.java revision b5560dbe16a8fb3148b0fb24c73836bf2e84dd61
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;
21014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringertimport com.android.quicksearchbox.ui.QueryTextView;
22185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionClickListener;
23185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsAdapter;
24185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsView;
253a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringertimport com.google.common.base.CharMatcher;
26185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
29dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringertimport android.content.DialogInterface;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
3121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringertimport android.database.DataSetObserver;
32d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinneyimport android.graphics.drawable.Drawable;
33fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
3511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport android.os.Debug;
364ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringertimport android.os.Handler;
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;
45014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringertimport android.view.inputmethod.CompletionInfo;
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
47145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringertimport android.widget.AbsListView;
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.ImageButton;
493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport java.io.File;
51014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringertimport java.util.ArrayList;
52014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringertimport java.util.Arrays;
53e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringertimport java.util.Collection;
545f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringertimport java.util.Set;
55ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
62754cb6b769c5955fc707a6c5ab6689b64df102b3Mathew Inwood    private static final boolean DBG = false;
630484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchActivity";
6411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    private static final boolean TRACE = false;
653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
66fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String SCHEME_CORPUS = "qsb.corpus";
67fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
68fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static final String INTENT_ACTION_QSB_AND_SELECT_CORPUS
69fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            = "com.android.quicksearchbox.action.QSB_AND_SELECT_CORPUS";
703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
71e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood    public static final String INTENT_ACTION_QSB_AND_HIDE_WIDGET_HINTS
72e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood            = "com.android.quicksearchbox.action.QSB_AND_HIDE_WIDGET_HINTS";
73e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood
74c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // The string used for privateImeOptions to identify to the IME that it should not show
75c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // a microphone button since one already exists in the search dialog.
76c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    // TODO: This should move to android-common or something.
77c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert    private static final String IME_OPTION_NO_MICROPHONE = "nm";
78c15f386b167efb3f7559f2f7ee362497040b7fe6Bjorn Bringert
799ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
80fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String INSTANCE_KEY_CORPUS = "corpus";
81839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    private static final String INSTANCE_KEY_QUERY = "query";
82713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
83f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    // Measures time from for last onCreate()/onNewIntent() call.
84f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private LatencyTracker mStartLatencyTracker;
85ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
86ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
87ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
88ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
89ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
90ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
91f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private CorpusSelectionDialog mCorpusSelectionDialog;
92f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
93185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    protected SuggestionsAdapter mSuggestionsAdapter;
94185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
9521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private CorporaObserver mCorporaObserver;
9621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
97014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    protected QueryTextView mQueryTextView;
98e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    // True if the query was empty on the previous call to updateQuery()
99e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected boolean mQueryWasEmpty = true;
1003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected SuggestionsView mSuggestionsView;
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mSearchGoButton;
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mVoiceSearchButton;
105a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert    protected ImageButton mCorpusIndicator;
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
107fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus mCorpus;
1082617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
109713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mUpdateSuggestions;
1103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
111d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Handler mHandler = new Handler();
112d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mUpdateSuggestionsTask = new Runnable() {
1134ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        public void run() {
1144ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert            updateSuggestions(getQuery());
1154ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        }
1164ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    };
1174ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
118d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mShowInputMethodTask = new Runnable() {
119d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        public void run() {
120d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            showInputMethodForQuery();
121d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
122d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    };
123d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
1253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
12711234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (TRACE) startMethodTracing();
128ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
131713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1326ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert        setContentView();
13366ee1cc883f77fee930587503ecdcd8d18f12b23Mathew Inwood        SuggestListFocusListener suggestionFocusListener = new SuggestListFocusListener();
134816804b67619af133860a1e28e92f58bc642260dBjorn Bringert        mSuggestionsAdapter = getQsbApplication().createSuggestionsAdapter();
13566ee1cc883f77fee930587503ecdcd8d18f12b23Mathew Inwood        mSuggestionsAdapter.setSuggestionClickListener(new ClickHandler());
13666ee1cc883f77fee930587503ecdcd8d18f12b23Mathew Inwood        mSuggestionsAdapter.setOnFocusChangeListener(suggestionFocusListener);
137185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
138014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mQueryTextView = (QueryTextView) findViewById(R.id.search_src_text);
1393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
140145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        mSuggestionsView.setOnScrollListener(new InputMethodCloser());
141713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
14266ee1cc883f77fee930587503ecdcd8d18f12b23Mathew Inwood        mSuggestionsView.setOnFocusChangeListener(suggestionFocusListener);
1433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
146a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator = (ImageButton) findViewById(R.id.corpus_indicator);
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
148782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
149782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
1507b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
151014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mQueryTextView.setSuggestionClickListener(new ClickHandler());
152782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
153fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnClickListener(new CorpusIndicatorClickListener());
1542617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
155782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
156782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
157782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
1587be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1597be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        ButtonsKeyListener buttonsKeyListener = new ButtonsKeyListener();
1607be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mSearchGoButton.setOnKeyListener(buttonsKeyListener);
1617be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        mVoiceSearchButton.setOnKeyListener(buttonsKeyListener);
162fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mCorpusIndicator.setOnKeyListener(buttonsKeyListener);
163713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1649ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUpdateSuggestions = true;
1659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1689ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1699ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1709ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1719ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
172014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mSuggestionsAdapter.registerDataSetObserver(new SuggestionsObserver());
173014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
174713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
175713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
176713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setAdapter(mSuggestionsAdapter);
17721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
17821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mCorporaObserver = new CorporaObserver();
17921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().registerDataSetObserver(mCorporaObserver);
1809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
181713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1826ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert    protected void setContentView() {
1836ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert        setContentView(R.layout.search_activity);
1846ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert    }
1856ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert
18611234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    private void startMethodTracing() {
18711234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        File traceDir = getDir("traces", 0);
18811234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        String traceFile = new File(traceDir, "qsb.trace").getAbsolutePath();
18911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        Debug.startMethodTracing(traceFile);
19011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    }
19111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert
1929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
194839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onNewIntent()");
195ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1969ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1979ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1989ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
200ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
201f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mStartLatencyTracker = new LatencyTracker();
202ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
203ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
204ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
205ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
2069ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
2079ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
208fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS);
209839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_QUERY);
21021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
211839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, false);
212713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
213713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
214713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
2159ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
2169ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
2179ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
2189ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
219fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
22021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        outState.putString(INSTANCE_KEY_CORPUS, getCorpusName());
221839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        outState.putString(INSTANCE_KEY_QUERY, getQuery());
222713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
223713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
224713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
225713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
22621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        String corpusName = getCorpusNameFromUri(intent.getData());
2272617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
2282617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
229839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        boolean selectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
2302617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
23121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
232839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, selectAll);
233c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mAppSearchData = appSearchData;
2342617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
23511c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        if (startedIntoCorpusSelectionDialog()) {
23641fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
2372617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
238e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood        if (INTENT_ACTION_QSB_AND_HIDE_WIDGET_HINTS.equals(intent.getAction())) {
239e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood            SearchWidgetProvider.hideHintsNow(getApplicationContext());
240e83204ae5e130fed7e7f0acdbca0ed29a9bbc934Mathew Inwood        }
2419ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
2429ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
24311c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    public boolean startedIntoCorpusSelectionDialog() {
24411c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        return INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(getIntent().getAction());
24511c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    }
24611c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert
247dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    /**
248dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * Removes corpus selector intent action, so that BACK works normally after
249dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * dismissing and reopening the corpus selector.
250dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     */
251dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    private void clearStartedIntoCorpusSelectionDialog() {
252dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        Intent oldIntent = getIntent();
253dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        if (SearchActivity.INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(oldIntent.getAction())) {
254dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            Intent newIntent = new Intent(oldIntent);
255dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            newIntent.setAction(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
256dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            setIntent(newIntent);
257dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
258dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
259dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
260fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static Uri getCorpusUri(Corpus corpus) {
261fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) return null;
262fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return new Uri.Builder()
263fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .scheme(SCHEME_CORPUS)
264fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .authority(corpus.getName())
265fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .build();
266fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
267fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
26821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusNameFromUri(Uri uri) {
269fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
270fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
27121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return uri.getAuthority();
272fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
273fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
274fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpus getCorpus(String sourceName) {
2750ccab5903ba2c64ec4cdd33dbcea91b1e279d556Bjorn Bringert        if (sourceName == null) return null;
276fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Corpus corpus = getCorpora().getCorpus(sourceName);
277fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) {
278fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            Log.w(TAG, "Unknown corpus " + sourceName);
2799ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
280713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
281fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return corpus;
282713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
283713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
28421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private void setCorpus(String corpusName) {
28521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        if (DBG) Log.d(TAG, "setCorpus(" + corpusName + ")");
28621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mCorpus = getCorpus(corpusName);
2879ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Drawable sourceIcon;
28821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        if (mCorpus == null) {
289c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert            sourceIcon = getCorpusViewFactory().getGlobalSearchIcon();
290713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        } else {
29121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            sourceIcon = mCorpus.getCorpusIcon();
292713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
29321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mSuggestionsAdapter.setCorpus(mCorpus);
294a576b1abd602c08dcf0d470facc43fac0243b07cBjorn Bringert        mCorpusIndicator.setImageDrawable(sourceIcon);
2953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2965691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateUi(getQuery().length() == 0);
2970484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    }
2980484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert
29921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusName() {
30021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return mCorpus == null ? null : mCorpus.getName();
30121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
30221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
3033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
30449fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert        return QsbApplication.get(this);
3053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3074ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Config getConfig() {
3084ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        return getQsbApplication().getConfig();
3094ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
3104ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
311fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Corpora getCorpora() {
312fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getQsbApplication().getCorpora();
313fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
314fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private ShortcutRepository getShortcutRepository() {
3163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getShortcutRepository();
3173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
319ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
32047d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert        return getQsbApplication().getSuggestionsProvider();
32147d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
32247d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
323c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert    private CorpusViewFactory getCorpusViewFactory() {
324c5a3ecb62318a6be1d1a626a36b52829e989f740Bjorn Bringert        return getQsbApplication().getCorpusViewFactory();
325185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
326185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
32749fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert    private VoiceSearch getVoiceSearch() {
32849fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert        return QsbApplication.get(this).getVoiceSearch();
32949fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert    }
33049fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert
331ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
332ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
333ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
334ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
3353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onDestroy();
33921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().unregisterDataSetObserver(mCorporaObserver);
340185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsView.setAdapter(null);  // closes mSuggestionsAdapter
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
3459ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
346f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (!mTookAction) {
347f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // TODO: This gets logged when starting other activities, e.g. by opening he search
348f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // settings, or clicking a notification in the status bar.
349f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logExit(getCurrentSuggestions(), getQuery().length());
350f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
3519ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
353185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
35494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        getQsbApplication().getShortcutRefresher().reset();
355f69873ae02e6d8f8a550f5fa43b5b1f2cbea96ffBjorn Bringert        dismissCorpusSelectionDialog();
3563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
3573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
360839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    protected void onRestart() {
361839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onRestart()");
362839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        super.onRestart();
363839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    }
364839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert
365839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    @Override
366b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
3679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
368b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
36911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        updateSuggestionsBuffered();
370dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        if (!isCorpusSelectionDialogShowing()) {
371dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            mQueryTextView.requestFocus();
372dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
37311234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (TRACE) Debug.stopMethodTracing();
374b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
375b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
376b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
3773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean onCreateOptionsMenu(Menu menu) {
3783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreateOptionsMenu(menu);
37926d85260316193a1aeff5d407d4f32f5297be012Bjorn Bringert        SearchSettings.addSearchSettingsMenuItem(this, menu);
3803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
3813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
383d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    @Override
384d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    public void onWindowFocusChanged(boolean hasFocus) {
385d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        super.onWindowFocusChanged(hasFocus);
386d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        if (hasFocus) {
387d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            // Launch the IME after a bit
388d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            mHandler.postDelayed(mShowInputMethodTask, 0);
389d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
390d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    }
391d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
392713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        CharSequence q = mQueryTextView.getText();
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return q == null ? "" : q.toString();
3953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
398014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions.
3993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setQuery(String query, boolean selectAll) {
4013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = false;
4023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView.setText(query);
403014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mQueryTextView.setTextSelection(selectAll);
4043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = true;
4053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
40718959800eddb7127e4fd9b24e91458d0584cc257Mathew Inwood    protected void updateUi(boolean queryEmpty) {
4085691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateQueryTextView(queryEmpty);
4095691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateSearchGoButton(queryEmpty);
4105691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        updateVoiceSearchButton(queryEmpty);
4115691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    }
4125691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert
413e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateQueryTextView(boolean queryEmpty) {
414e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
41520c51f89fdba5105efdf0a37f50bf7fa013d5b8dMathew Inwood            if (isSearchCorpusWeb()) {
4165691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty_google);
4175691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(null);
4185691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            } else {
4195691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setBackgroundResource(R.drawable.textfield_search_empty);
4205691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                mQueryTextView.setHint(mCorpus.getHint());
4215691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert            }
422e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
423e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setBackgroundResource(R.drawable.textfield_search);
424e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
425e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
426e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
427e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    private void updateSearchGoButton(boolean queryEmpty) {
428e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        if (queryEmpty) {
429e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.GONE);
430e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
431e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mSearchGoButton.setVisibility(View.VISIBLE);
432e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
433e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
434e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
435e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    protected void updateVoiceSearchButton(boolean queryEmpty) {
43649fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert        if (queryEmpty && getVoiceSearch().shouldShowVoiceSearch(mCorpus)) {
437e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.VISIBLE);
438e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
439e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        } else {
440e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mVoiceSearchButton.setVisibility(View.GONE);
441e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            mQueryTextView.setPrivateImeOptions(null);
442e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert        }
443e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
444e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
44541fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert    protected void showCorpusSelectionDialog() {
446f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog == null) {
447f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog = new CorpusSelectionDialog(this);
448f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOwnerActivity(this);
449dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            mCorpusSelectionDialog.setOnDismissListener(new CorpusSelectorDismissListener());
450f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.setOnCorpusSelectedListener(new CorpusSelectionListener());
4512617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
452f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mCorpusSelectionDialog.show(mCorpus);
4532617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4542617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
455dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    protected boolean isCorpusSelectionDialogShowing() {
456dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        return mCorpusSelectionDialog != null && mCorpusSelectionDialog.isShowing();
457dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
458dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
459f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    protected void dismissCorpusSelectionDialog() {
460f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (mCorpusSelectionDialog != null) {
461f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mCorpusSelectionDialog.dismiss();
4622617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
4632617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4642617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
465dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    /**
466dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     * @return true if a search was performed as a result of this click, false otherwise.
467dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     */
468dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    protected boolean onSearchClicked(int method) {
4693a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringert        String query = CharMatcher.WHITESPACE.trimAndCollapseFrom(getQuery(), ' ');
4703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
471eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
472eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        // Don't do empty queries
473dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (TextUtils.getTrimmedLength(query) == 0) return false;
474eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
47581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
476dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (searchCorpus == null) return false;
477c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
478ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
479c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
480c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log search start
481fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getLogger().logSearch(mCorpus, method, query.length());
482c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
483c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
484c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        SuggestionData searchShortcut = searchCorpus.createSearchShortcut(query);
485c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchShortcut != null) {
48693bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert            ListSuggestionCursor cursor = new ListSuggestionCursor(query);
487c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            cursor.add(searchShortcut);
488c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert            getShortcutRepository().reportClick(cursor, 0);
489c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        }
490c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
491c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start search
492c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createSearchIntent(query, mAppSearchData);
49381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
494dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        return true;
4953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
4983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
49981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
500c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchCorpus == null) return;
501c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
502535931433926d342c6277034cad91143ae28b72dBjorn Bringert        mTookAction = true;
503535931433926d342c6277034cad91143ae28b72dBjorn Bringert
504c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log voice search start
505c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        getLogger().logVoiceSearch(searchCorpus);
506ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
507c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start voice search
508c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createVoiceSearchIntent(mAppSearchData);
50981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
51081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
51181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
51281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    /**
51381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     * Gets the corpus to use for any searches. This is the web corpus in "All" mode,
51481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     * and the selected corpus otherwise.
51581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert     */
516ca9c7b7bc0183aa1b6d160bf76597c96e70831f6Mathew Inwood    protected Corpus getSearchCorpus() {
51781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (mCorpus != null) {
51881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return mCorpus;
51981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } else {
52081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Corpus webCorpus = getCorpora().getWebCorpus();
52181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            if (webCorpus == null) {
52281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert                Log.e(TAG, "No web corpus");
52381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            }
52481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return webCorpus;
52581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
526c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert    }
527c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
528014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    /**
529014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     * Checks if the corpus used for typed searchs is the web corpus.
530014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     */
531014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    protected boolean isSearchCorpusWeb() {
532014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        Corpus corpus = getSearchCorpus();
533014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        return corpus != null && corpus.isWebCorpus();
534014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
535014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
536e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    protected SuggestionCursor getCurrentSuggestions() {
537fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSuggestionsAdapter.getCurrentSuggestions();
538782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
539782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
5405f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    protected SuggestionCursor getCurrentSuggestions(int position) {
5415f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        SuggestionCursor suggestions = getCurrentSuggestions();
5425f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (suggestions == null) {
5435f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
5445f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
5455f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        int count = suggestions.getCount();
5465f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (position < 0 || position >= count) {
5475f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            Log.w(TAG, "Invalid suggestion position " + position + ", count = " + count);
5485f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
5495f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
5505f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        suggestions.moveTo(position);
5515f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        return suggestions;
5525f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
5535f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
5545f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    protected Set<Corpus> getCurrentIncludedCorpora() {
5555f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        Suggestions suggestions = mSuggestionsAdapter.getSuggestions();
5565f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        return suggestions == null ? null : suggestions.getIncludedCorpora();
5575f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
5585f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
55981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    protected void launchIntent(Intent intent) {
56081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (intent == null) {
56181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return;
56281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
56381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        try {
56481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            startActivity(intent);
56581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } catch (RuntimeException ex) {
56681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // Since the intents for suggestions specified by suggestion providers,
56781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // guard against them not being handled, not allowed, etc.
56881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Log.e(TAG, "Failed to start " + intent.toUri(0), ex);
56981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
57081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
57181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
572fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean launchSuggestion(int position) {
5735f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        SuggestionCursor suggestions = getCurrentSuggestions(position);
5745f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (suggestions == null) return false;
5756f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert
576fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Launching suggestion " + position);
577ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
578e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert
579c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log suggestion click
5805f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        getLogger().logSuggestionClick(position, suggestions, getCurrentIncludedCorpora());
581ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
582c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
583fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        getShortcutRepository().reportClick(suggestions, position);
584c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
585c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Launch intent
58681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        suggestions.moveTo(position);
58793bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        Intent intent = SuggestionUtils.getSuggestionIntent(suggestions, mAppSearchData);
58881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
589c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
5903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
5913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
593fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionLongClicked(int position) {
594fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (DBG) Log.d(TAG, "Long clicked on suggestion " + position);
5953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return false;
5963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
598fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected boolean onSuggestionKeyDown(int position, int keyCode, KeyEvent event) {
599782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Treat enter or search as a click
600782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
601fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return launchSuggestion(position);
602782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
603782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
604782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return false;
605782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
606782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
6075f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    protected void refineSuggestion(int position) {
6085f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (DBG) Log.d(TAG, "query refine clicked, pos " + position);
6095f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        SuggestionCursor suggestions = getCurrentSuggestions(position);
6105f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (suggestions == null) {
6115f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
6125f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
6135f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        String query = suggestions.getSuggestionQuery();
6145f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (TextUtils.isEmpty(query)) {
6155f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
6165f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
6175f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
6185f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Log refine click
6195f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        getLogger().logRefine(position, suggestions, getCurrentIncludedCorpora());
6205f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
6215f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Put query + space in query text view
6225f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        String queryWithSpace = query + ' ';
6235f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        setQuery(queryWithSpace, false);
6245f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        updateSuggestions(queryWithSpace);
6255f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        mQueryTextView.requestFocus();
6265f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
6275f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
628782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected int getSelectedPosition() {
629782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedPosition();
630782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
631782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
6323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method.
6343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void hideInputMethod() {
636014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mQueryTextView.hideInputMethod();
6373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6397b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    protected void showInputMethodForQuery() {
640014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        mQueryTextView.showInputMethod();
6417b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
6427b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
643f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood    protected void onSuggestionListFocusChange(boolean focused) {
644f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood    }
645f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood
646f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood    protected void onQueryTextViewFocusChange(boolean focused) {
647f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood    }
648f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood
6493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
6503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method when the suggestions get focus.
6513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
6533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
6547b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Suggestions focus change, now: " + focused);
6557b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
6567b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                // The suggestions list got focus, hide the input method
6577b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                hideInputMethod();
6587b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            }
659f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood            onSuggestionListFocusChange(focused);
6607b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        }
6617b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    }
6627b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert
6637b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert    private class QueryTextViewFocusListener implements OnFocusChangeListener {
6647b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert        public void onFocusChange(View v, boolean focused) {
6657b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (DBG) Log.d(TAG, "Query focus change, now: " + focused);
6667b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert            if (focused) {
667dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert                // The query box got focus, show the input method
6687b9d59cd76eecd7645072c6728e2faf9ff9e180aBjorn Bringert                showInputMethodForQuery();
6693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
670f5e1499b48ac06de4e6e6460a7f14c3a7ac4eaddMathew Inwood            onQueryTextViewFocusChange(focused);
6713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
674e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney    private int getMaxSuggestions() {
675e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney        Config config = getConfig();
676e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney        return mCorpus == null
677e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney                ? config.getMaxPromotedSuggestions()
678e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney                : config.getMaxResultsPerSource();
679e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney    }
680e6f46632869a288c82b75187af166931f82a6e12Bryan Mawhinney
6814ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private void updateSuggestionsBuffered() {
682d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.removeCallbacks(mUpdateSuggestionsTask);
6834ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        long delay = getConfig().getTypingUpdateSuggestionsDelayMillis();
684d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.postDelayed(mUpdateSuggestionsTask, delay);
6854ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
6864ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
6876ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert    protected void updateSuggestions(String query) {
6883cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood
6893a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringert        query = CharMatcher.WHITESPACE.trimLeadingFrom(query);
6902fd5aa82682f077660faa8ec60c04238026a8731Mathew Inwood        if (DBG) Log.d(TAG, "getSuggestions(\""+query+"\","+mCorpus + ","+getMaxSuggestions()+")");
6913cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood        Suggestions suggestions = getSuggestionsProvider().getSuggestions(
6923cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood                query, mCorpus, getMaxSuggestions());
6933cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood
69411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        // Log start latency if this is the first suggestions update
69511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (mStarting) {
69611234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            mStarting = false;
69711234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
69811234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            int latency = mStartLatencyTracker.getLatency();
6993cb8178193a41f6c74ee396c318385a50dd624e1Mathew Inwood            getLogger().logStart(latency, source, mCorpus, suggestions.getExpectedCorpora());
700b5560dbe16a8fb3148b0fb24c73836bf2e84dd61Mathew Inwood            getQsbApplication().onStartupComplete();
70111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        }
70211234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert
703185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
7043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
706014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    /**
707014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     * If the input method is in fullscreen mode, and the selector corpus
708014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     * is All or Web, use the web search suggestions as completions.
709014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert     */
710014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    protected void updateInputMethodSuggestions() {
711014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
712014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        if (imm == null || !imm.isFullscreenMode()) return;
713014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        Suggestions suggestions = mSuggestionsAdapter.getSuggestions();
714014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        if (suggestions == null) return;
715014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        SuggestionCursor cursor = suggestions.getPromoted();
716014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        if (cursor == null) return;
717014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        CompletionInfo[] completions = webSuggestionsToCompletions(cursor);
718014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        if (DBG) Log.d(TAG, "displayCompletions(" + Arrays.toString(completions) + ")");
719014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        imm.displayCompletions(mQueryTextView, completions);
720014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
721014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
722014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    private CompletionInfo[] webSuggestionsToCompletions(SuggestionCursor cursor) {
723014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        int count = cursor.getCount();
724014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        ArrayList<CompletionInfo> completions = new ArrayList<CompletionInfo>(count);
72520c51f89fdba5105efdf0a37f50bf7fa013d5b8dMathew Inwood        boolean usingWebCorpus = isSearchCorpusWeb();
726014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        for (int i = 0; i < count; i++) {
727014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert            cursor.moveTo(i);
72820c51f89fdba5105efdf0a37f50bf7fa013d5b8dMathew Inwood            if (!usingWebCorpus || cursor.isWebSearchSuggestion()) {
729014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert                String text1 = cursor.getSuggestionText1();
730014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert                completions.add(new CompletionInfo(i, i, text1));
731014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert            }
732014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        }
733014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        return completions.toArray(new CompletionInfo[completions.size()]);
734014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
735014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
7367be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
7377be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        if (!event.isSystem() && !isDpadKey(keyCode)) {
7387be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
7397be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            if (mQueryTextView.requestFocus()) {
7407be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return mQueryTextView.dispatchKeyEvent(event);
7417be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            }
7427be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
7437be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        return false;
7447be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
7457be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
7467be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    private boolean isDpadKey(int keyCode) {
7477be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        switch (keyCode) {
7487be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_UP:
7497be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_DOWN:
7507be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_LEFT:
7517be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_RIGHT:
7527be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            case KeyEvent.KEYCODE_DPAD_CENTER:
7537be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return true;
7547be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            default:
7557be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert                return false;
7567be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert        }
7577be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert    }
7587be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
7593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Filters the suggestions list when the search text changes.
7613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
7633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void afterTextChanged(Editable s) {
764e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            boolean empty = s.length() == 0;
765e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            if (empty != mQueryWasEmpty) {
766e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert                mQueryWasEmpty = empty;
7675691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert                updateUi(empty);
768e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert            }
7693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mUpdateSuggestions) {
7704ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert                updateSuggestionsBuffered();
7713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
7723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
7753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
7783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
7823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Handles non-text keys in the query text view.
7833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
7843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class QueryTextViewKeyListener implements View.OnKeyListener {
7853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public boolean onKey(View view, int keyCode, KeyEvent event) {
7863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Handle IME search action key
7873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
788d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood                // if no action was taken, consume the key event so that the keyboard
789d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood                // remains on screen.
790dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood                return !onSearchClicked(Logger.SEARCH_METHOD_KEYBOARD);
7913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
7923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return false;
7933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
7943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
7953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
796782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
797782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the search and voice search buttons,
798782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * by refocusing to EditText.
799782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
800782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
801782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
8027be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
803782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
804782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
805782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
806782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
807782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the suggestions list view.
808782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
809713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private class SuggestionsViewKeyListener implements View.OnKeyListener {
810782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
811782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN) {
812fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                int position = getSelectedPosition();
813fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                if (onSuggestionKeyDown(position, keyCode, event)) {
814839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert                    return true;
815782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
816782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
8177be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
818782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
819782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
820782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
821145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert    private class InputMethodCloser implements SuggestionsView.OnScrollListener {
822145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
823145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
824145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert                int totalItemCount) {
825145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        }
826145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
827145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert        public void onScrollStateChanged(AbsListView view, int scrollState) {
828185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            hideInputMethod();
829185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        }
830185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
831185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
832185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
833fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public void onSuggestionClicked(int position) {
834fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           launchSuggestion(position);
8353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
8363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
837fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert       public boolean onSuggestionLongClicked(int position) {
838fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert           return SearchActivity.this.onSuggestionLongClicked(position);
839713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       }
840145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
841145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert       public void onSuggestionQueryRefineClicked(int position) {
8425f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert           refineSuggestion(position);
843145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert       }
8443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
8463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
8472617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the source selector.
8483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
8493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
8503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
851ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert            onSearchClicked(Logger.SEARCH_METHOD_BUTTON);
8523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
8533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
8553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
8562617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     * Listens for clicks on the search button.
8572617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert     */
858fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private class CorpusIndicatorClickListener implements View.OnClickListener {
8592617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        public void onClick(View view) {
86041fe9d1fc461c42907250d44d3537909e4c46a7dBjorn Bringert            showCorpusSelectionDialog();
8612617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
862f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    }
863f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
864dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    private class CorpusSelectorDismissListener implements DialogInterface.OnDismissListener {
865dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        public void onDismiss(DialogInterface dialog) {
866dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            if (DBG) Log.d(TAG, "Corpus selector dismissed");
867dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            clearStartedIntoCorpusSelectionDialog();
868dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
869dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
870dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
871f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private class CorpusSelectionListener
872f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            implements CorpusSelectionDialog.OnCorpusSelectedListener {
87321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        public void onCorpusSelected(String corpusName) {
87421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            setCorpus(corpusName);
875f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            updateSuggestions(getQuery());
876f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            mQueryTextView.requestFocus();
877dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            showInputMethodForQuery();
878f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
8792617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
8802617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
8812617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    /**
8823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the voice search button.
8833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
8843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class VoiceSearchButtonClickListener implements View.OnClickListener {
8853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
8863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onVoiceSearchClicked();
8873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
8883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
8893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
89021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private class CorporaObserver extends DataSetObserver {
89121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        @Override
89221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        public void onChanged() {
89321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            setCorpus(getCorpusName());
89421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            updateSuggestions(getQuery());
89521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        }
89621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
89721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
898014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    private class SuggestionsObserver extends DataSetObserver {
899014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        @Override
900014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        public void onChanged() {
901014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert            updateInputMethodSuggestions();
902014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert        }
903014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
904014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
9053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
906