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;
207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport com.android.quicksearchbox.ui.SearchActivityView;
21185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionClickListener;
22185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsAdapter;
23b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringertimport com.android.quicksearchbox.util.Consumer;
24b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringertimport com.android.quicksearchbox.util.Consumers;
252607687d72deb8e06329597ab8bffcca9c746153Mathew Inwoodimport com.google.common.annotations.VisibleForTesting;
263a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringertimport com.google.common.base.CharMatcher;
27185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
293bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringertimport android.app.AlertDialog;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
31dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringertimport android.content.DialogInterface;
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
3321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringertimport android.database.DataSetObserver;
34fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
3611234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport android.os.Debug;
374ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringertimport android.os.Handler;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
42fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringertimport android.widget.Toast;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport java.io.File;
45dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringertimport java.util.ArrayList;
464572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwoodimport java.util.Collection;
47b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringertimport java.util.List;
485f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringertimport java.util.Set;
49ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
56754cb6b769c5955fc707a6c5ab6689b64df102b3Mathew Inwood    private static final boolean DBG = false;
570484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchActivity";
583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
59fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String SCHEME_CORPUS = "qsb.corpus";
60fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
61fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static final String INTENT_ACTION_QSB_AND_SELECT_CORPUS
62fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            = "com.android.quicksearchbox.action.QSB_AND_SELECT_CORPUS";
633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6448ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert    private static final String INTENT_EXTRA_TRACE_START_UP = "trace_start_up";
6548ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert
669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
67fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String INSTANCE_KEY_CORPUS = "corpus";
68839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    private static final String INSTANCE_KEY_QUERY = "query";
69713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
7071f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert    private static final String ACTIVITY_HELP_CONTEXT = "search";
7171f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert
7248ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert    private boolean mTraceStartUp;
73f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    // Measures time from for last onCreate()/onNewIntent() call.
74f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private LatencyTracker mStartLatencyTracker;
7598cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    // Measures time spent inside onCreate()
7698cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private LatencyTracker mOnCreateTracker;
7798cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private int mOnCreateLatency;
78ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
79ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
80ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
81ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
82ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
83ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
847010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private SearchActivityView mSearchActivityView;
85185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
8621bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private CorporaObserver mCorporaObserver;
8721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
882617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
90d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Handler mHandler = new Handler();
91d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mUpdateSuggestionsTask = new Runnable() {
924ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        public void run() {
937f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert            updateSuggestions();
944ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        }
954ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    };
964ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
97d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mShowInputMethodTask = new Runnable() {
98d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        public void run() {
997010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mSearchActivityView.showInputMethodForQuery();
100d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
101d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    };
102d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
1032607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    private OnDestroyListener mDestroyListener;
1042607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
1053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
10848ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        mTraceStartUp = getIntent().hasExtra(INTENT_EXTRA_TRACE_START_UP);
10948ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        if (mTraceStartUp) {
11048ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            String traceFile = new File(getDir("traces", 0), "qsb-start.trace").getAbsolutePath();
11148ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            Log.i(TAG, "Writing start-up trace to " + traceFile);
11248ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            Debug.startMethodTracing(traceFile);
11348ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        }
114ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
117713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
118c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        // This forces the HTTP request to check the users domain to be
119c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        // sent as early as possible.
120c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        QsbApplication.get(this).getSearchBaseUrlHelper();
121c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
1225880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        mSearchActivityView = setupContentView();
123aa7d79792baca59eb7afe00ea27abc5176ddd34bMathew Inwood
124fdb80c2962c88ac62dcd7ee7f2fab1857b61506bMathew Inwood        if (getConfig().showScrollingSuggestions()) {
125f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood            mSearchActivityView.setMaxPromotedSuggestions(getConfig().getMaxPromotedSuggestions());
126fdb80c2962c88ac62dcd7ee7f2fab1857b61506bMathew Inwood        } else {
127fdb80c2962c88ac62dcd7ee7f2fab1857b61506bMathew Inwood            mSearchActivityView.limitSuggestionsToViewHeight();
128fdb80c2962c88ac62dcd7ee7f2fab1857b61506bMathew Inwood        }
129f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        if (getConfig().showScrollingResults()) {
1307a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            mSearchActivityView.setMaxPromotedResults(getConfig().getMaxPromotedResults());
131f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        } else {
132f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood            mSearchActivityView.limitResultsToViewHeight();
133f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        }
134848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
1357010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSearchClickListener(new SearchActivityView.SearchClickListener() {
1367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public boolean onSearchClicked(int method) {
1377010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return SearchActivity.this.onSearchClicked(method);
1387010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1397010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
1402617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
1417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setQueryListener(new SearchActivityView.QueryListener() {
1427010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public void onQueryChanged() {
1437010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                updateSuggestionsBuffered();
1447010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1457010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
146782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
1477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSuggestionClickListener(new ClickHandler());
1487be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setVoiceSearchButtonClickListener(new View.OnClickListener() {
1507010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public void onClick(View view) {
1517010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                onVoiceSearchClicked();
1527010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
1549ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
155e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        View.OnClickListener finishOnClick = new View.OnClickListener() {
1569e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            public void onClick(View v) {
1579e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood                finish();
1589e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            }
159e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        };
160e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        mSearchActivityView.setExitClickListener(finishOnClick);
1619e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood
1629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1639ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1649ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
168713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
169713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
1707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.start();
17121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
17221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        mCorporaObserver = new CorporaObserver();
17321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().registerDataSetObserver(mCorporaObserver);
17498cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        recordOnCreateDone();
1759ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
176713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1775880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected SearchActivityView setupContentView() {
1786ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert        setContentView(R.layout.search_activity);
1795880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        return (SearchActivityView) findViewById(R.id.search_activity_view);
1805880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    }
1815880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert
1825880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected SearchActivityView getSearchActivityView() {
1835880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        return mSearchActivityView;
1846ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert    }
1856ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert
1869ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1879ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
188839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onNewIntent()");
189ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1909ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
194ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
195f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mStartLatencyTracker = new LatencyTracker();
19698cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        mOnCreateTracker = new LatencyTracker();
197ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
198ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
199ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
200ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
20198cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private void recordOnCreateDone() {
20298cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        mOnCreateLatency = mOnCreateTracker.getLatency();
20398cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    }
20498cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood
2059ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
2069ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
207fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS);
208839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_QUERY);
20921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
210839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, false);
211713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
212713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
213713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
2149ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
2159ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
2169ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
2179ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
218fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
21921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        outState.putString(INSTANCE_KEY_CORPUS, getCorpusName());
220839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        outState.putString(INSTANCE_KEY_QUERY, getQuery());
221713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
222713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
223713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
224713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
22521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        String corpusName = getCorpusNameFromUri(intent.getData());
2262617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
2272617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
228839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        boolean selectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
2292617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
23021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        setCorpus(corpusName);
231839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, selectAll);
232c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mAppSearchData = appSearchData;
2332617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
23411c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        if (startedIntoCorpusSelectionDialog()) {
2350a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood            mSearchActivityView.showCorpusSelectionDialog();
2362617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
2379ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
2389ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
23911c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    public boolean startedIntoCorpusSelectionDialog() {
24011c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert        return INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(getIntent().getAction());
24111c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert    }
24211c44a74cb2519c0c8630486784aa916b964070eBjorn Bringert
243dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    /**
244dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * Removes corpus selector intent action, so that BACK works normally after
245dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     * dismissing and reopening the corpus selector.
246dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert     */
2470a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    public void clearStartedIntoCorpusSelectionDialog() {
248dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        Intent oldIntent = getIntent();
249dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        if (SearchActivity.INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(oldIntent.getAction())) {
250dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            Intent newIntent = new Intent(oldIntent);
251dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            newIntent.setAction(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
252dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            setIntent(newIntent);
253dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
254dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
255dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
256fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public static Uri getCorpusUri(Corpus corpus) {
257fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (corpus == null) return null;
258fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return new Uri.Builder()
259fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .scheme(SCHEME_CORPUS)
260fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .authority(corpus.getName())
261fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                .build();
262fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
263fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
26421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusNameFromUri(Uri uri) {
265fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
266fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
26721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return uri.getAuthority();
268fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
269fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
2700a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    private Corpus getCorpus() {
2710a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        return mSearchActivityView.getCorpus();
272713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
273713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
2740a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    private String getCorpusName() {
2750a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        return mSearchActivityView.getCorpusName();
276f165f2d6c6f8173b94937ae3202f0b638348dffdBjorn Bringert    }
277f165f2d6c6f8173b94937ae3202f0b638348dffdBjorn Bringert
2780a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    private void setCorpus(String name) {
2790a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchActivityView.setCorpus(name);
28021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
28121bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
2823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
28349fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert        return QsbApplication.get(this);
2843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2864ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Config getConfig() {
2874ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        return getQsbApplication().getConfig();
2884ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
2894ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
29096fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    protected SearchSettings getSettings() {
29196fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert        return getQsbApplication().getSettings();
29296fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    }
29396fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert
294b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private Corpora getCorpora() {
295b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        return getQsbApplication().getCorpora();
296fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
297fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
298b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private CorpusRanker getCorpusRanker() {
299b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        return getQsbApplication().getCorpusRanker();
3003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
302b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private ShortcutRepository getShortcutRepository() {
303b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        return getQsbApplication().getShortcutRepository();
304848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    }
305848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
306b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
307b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        return getQsbApplication().getSuggestionsProvider();
30847d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
30947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
310ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
311ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
312ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
313ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
3142607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    @VisibleForTesting
3152607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    public void setOnDestroyListener(OnDestroyListener l) {
3162607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        mDestroyListener = l;
3172607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    }
3182607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
3193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
3213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
32221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        getCorpora().unregisterDataSetObserver(mCorporaObserver);
3237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.destroy();
3242607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        super.onDestroy();
3252607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        if (mDestroyListener != null) {
3262607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood            mDestroyListener.onDestroyed();
3272607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        }
3283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
3329ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
333f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (!mTookAction) {
334848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani            // TODO: This gets logged when starting other activities, e.g. by opening the search
335f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // settings, or clicking a notification in the status bar.
336aa7d79792baca59eb7afe00ea27abc5176ddd34bMathew Inwood            // TODO we should log both sets of suggestions in 2-pane mode
337f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logExit(getCurrentSuggestions(), getQuery().length());
338f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
3399ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
3403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
3417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.clearSuggestions();
34294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        getQsbApplication().getShortcutRefresher().reset();
3430a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchActivityView.onStop();
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
3482e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    protected void onPause() {
3492e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        if (DBG) Log.d(TAG, "onPause()");
3502e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        mSearchActivityView.onPause();
3512e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        super.onPause();
3522e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    }
3532e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani
3542e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    @Override
355839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    protected void onRestart() {
356839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onRestart()");
357839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        super.onRestart();
358839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    }
359839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert
360839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    @Override
361b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
3629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
363b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
36411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        updateSuggestionsBuffered();
3650a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchActivityView.onResume();
36648ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        if (mTraceStartUp) Debug.stopMethodTracing();
367b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
368b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
369b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
37073a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert    public boolean onPrepareOptionsMenu(Menu menu) {
37173a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        // Since the menu items are dynamic, we recreate the menu every time.
37273a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        menu.clear();
37373a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        createMenuItems(menu, true);
37496fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert        return true;
37596fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    }
37696fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert
37773a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert    public void createMenuItems(Menu menu, boolean showDisabled) {
37873a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        getSettings().addMenuItems(menu, showDisabled);
37971f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert        getQsbApplication().getHelp().addHelpMenuItem(menu, ACTIVITY_HELP_CONTEXT);
3805eee22a4ec6fc45deb9706ba535039ccae51b90aBjorn Bringert    }
3815eee22a4ec6fc45deb9706ba535039ccae51b90aBjorn Bringert
38296fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    @Override
383d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    public void onWindowFocusChanged(boolean hasFocus) {
384d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        super.onWindowFocusChanged(hasFocus);
385d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        if (hasFocus) {
386d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            // Launch the IME after a bit
387d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            mHandler.postDelayed(mShowInputMethodTask, 0);
388d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
389d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    }
390d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
391713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3927010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return mSearchActivityView.getQuery();
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3957010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void setQuery(String query, boolean selectAll) {
3967010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setQuery(query, selectAll);
397e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
398e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
3990a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    public CorpusSelectionDialog getCorpusSelectionDialog() {
4000a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        CorpusSelectionDialog dialog = createCorpusSelectionDialog();
4010a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        dialog.setOwnerActivity(this);
4020a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        dialog.setOnDismissListener(new CorpusSelectorDismissListener());
4030a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        return dialog;
4042617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    }
4052617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
40623db3f9877e691568fe2c955fd9d4f48a2eb937aMathew Inwood    protected CorpusSelectionDialog createCorpusSelectionDialog() {
40796fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert        return new CorpusSelectionDialog(this, getSettings());
40835c43805a450c17d5fc5e27dc073edc6e2cd7320Amith Yamasani    }
40935c43805a450c17d5fc5e27dc073edc6e2cd7320Amith Yamasani
410dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    /**
411dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     * @return true if a search was performed as a result of this click, false otherwise.
412dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     */
413dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    protected boolean onSearchClicked(int method) {
4143a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringert        String query = CharMatcher.WHITESPACE.trimAndCollapseFrom(getQuery(), ' ');
4153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
416eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
417eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        // Don't do empty queries
418dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (TextUtils.getTrimmedLength(query) == 0) return false;
419eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
42081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
421dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (searchCorpus == null) return false;
422c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
423ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
424c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
425c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log search start
4260a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        getLogger().logSearch(getCorpus(), method, query.length());
427c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
428c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start search
4299d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert        startSearch(searchCorpus, query);
4309d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert        return true;
4319d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    }
4329d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
4339d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    protected void startSearch(Corpus searchCorpus, String query) {
434c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createSearchIntent(query, mAppSearchData);
43581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
4363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
4393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
44081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Corpus searchCorpus = getSearchCorpus();
441c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        if (searchCorpus == null) return;
442c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
443535931433926d342c6277034cad91143ae28b72dBjorn Bringert        mTookAction = true;
444535931433926d342c6277034cad91143ae28b72dBjorn Bringert
445c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log voice search start
446c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        getLogger().logVoiceSearch(searchCorpus);
447ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
448c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start voice search
449c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        Intent intent = searchCorpus.createVoiceSearchIntent(mAppSearchData);
45081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
45181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
45281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
453ca9c7b7bc0183aa1b6d160bf76597c96e70831f6Mathew Inwood    protected Corpus getSearchCorpus() {
4547010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return mSearchActivityView.getSearchCorpus();
455014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
456014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
457e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    protected SuggestionCursor getCurrentSuggestions() {
458fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood        return mSearchActivityView.getCurrentPromotedSuggestions();
459782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
460782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4617a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected SuggestionPosition getCurrentSuggestions(SuggestionsAdapter<?> adapter, long id) {
4627a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition pos = adapter.getSuggestion(id);
4637a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (pos == null) {
4647a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            return null;
4657a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        }
4667a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionCursor suggestions = pos.getCursor();
4677a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        int position = pos.getPosition();
4685f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (suggestions == null) {
4695f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
4705f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
4715f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        int count = suggestions.getCount();
4725f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (position < 0 || position >= count) {
4735f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            Log.w(TAG, "Invalid suggestion position " + position + ", count = " + count);
4745f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
4755f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
4765f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        suggestions.moveTo(position);
4777a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        return pos;
4785f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
4795f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
4805f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    protected Set<Corpus> getCurrentIncludedCorpora() {
4817010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        Suggestions suggestions = mSearchActivityView.getSuggestions();
482848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        return suggestions == null  ? null : suggestions.getIncludedCorpora();
4835f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
4845f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
48581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    protected void launchIntent(Intent intent) {
48613b4f2dc4b339790c2b9b0220be47c8e77fd61eaMathew Inwood        if (DBG) Log.d(TAG, "launchIntent " + intent);
48781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (intent == null) {
48881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return;
48981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
49081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        try {
49181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            startActivity(intent);
49281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } catch (RuntimeException ex) {
49381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // Since the intents for suggestions specified by suggestion providers,
49481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // guard against them not being handled, not allowed, etc.
49581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Log.e(TAG, "Failed to start " + intent.toUri(0), ex);
49681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
49781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
49881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
4997a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    private boolean launchSuggestion(SuggestionsAdapter<?> adapter, long id) {
5007a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
5017a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) return false;
5026f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert
5037a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (DBG) Log.d(TAG, "Launching suggestion " + id);
504ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
505e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert
506c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log suggestion click
5077a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(),
508c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert                Logger.SUGGESTION_CLICK_TYPE_LAUNCH);
509ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
510c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Create shortcut
5117a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        getShortcutRepository().reportClick(suggestion.getCursor(), suggestion.getPosition());
512c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
513c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Launch intent
5147a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        launchSuggestion(suggestion.getCursor(), suggestion.getPosition());
5159d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
5169d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert        return true;
5179d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    }
5189d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
5199d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    protected void launchSuggestion(SuggestionCursor suggestions, int position) {
52081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        suggestions.moveTo(position);
52193bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        Intent intent = SuggestionUtils.getSuggestionIntent(suggestions, mAppSearchData);
52281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
5233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5253bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert    protected void removeFromHistoryClicked(final SuggestionsAdapter<?> adapter,
5263bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert            final long id) {
5273bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
5283bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert        if (suggestion == null) return;
5293bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert        CharSequence title = suggestion.getSuggestionText1();
5303bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert        AlertDialog dialog = new AlertDialog.Builder(this)
5313bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                .setTitle(title)
5323bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                .setMessage(R.string.remove_from_history)
5333bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                .setPositiveButton(android.R.string.ok,
5343bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                        new DialogInterface.OnClickListener() {
5353bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                            public void onClick(DialogInterface dialog, int which) {
5363bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                                // TODO: what if the suggestions have changed?
5373bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                                removeFromHistory(adapter, id);
5383bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                            }
5393bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                        })
5403bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                .setNegativeButton(android.R.string.cancel, null)
5413bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert                .create();
5423bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert        dialog.show();
5433bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert    }
5443bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert
5457a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected void removeFromHistory(SuggestionsAdapter<?> adapter, long id) {
5467a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
5477a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) return;
5487a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        removeFromHistory(suggestion.getCursor(), suggestion.getPosition());
549fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        // TODO: Log to event log?
550fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    }
551fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert
552fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    protected void removeFromHistory(SuggestionCursor suggestions, int position) {
553fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        removeShortcut(suggestions, position);
554fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        removeFromHistoryDone(true);
555fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    }
556fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert
557fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    protected void removeFromHistoryDone(boolean ok) {
558fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        Log.i(TAG, "Removed query from history, success=" + ok);
559fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        updateSuggestionsBuffered();
560fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        if (!ok) {
561fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert            Toast.makeText(this, R.string.remove_from_history_failed, Toast.LENGTH_SHORT).show();
562fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        }
563fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    }
564fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert
565fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    protected void removeShortcut(SuggestionCursor suggestions, int position) {
566fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        if (suggestions.isSuggestionShortcut()) {
567fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert            if (DBG) Log.d(TAG, "Removing suggestion " + position + " from shortcuts");
568fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert            getShortcutRepository().removeFromHistory(suggestions, position);
569fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        }
570fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    }
571fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert
5727a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected void clickedQuickContact(SuggestionsAdapter<?> adapter, long id) {
5737a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
5747a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) return;
575c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert
5767a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (DBG) Log.d(TAG, "Used suggestion " + suggestion.getPosition());
577c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert        mTookAction = true;
578c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert
579c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert        // Log suggestion click
5807a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(),
581c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert                Logger.SUGGESTION_CLICK_TYPE_QUICK_CONTACT);
582c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert
583c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert        // Create shortcut
5847a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        getShortcutRepository().reportClick(suggestion.getCursor(), suggestion.getPosition());
585c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert    }
586c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert
5877a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected void refineSuggestion(SuggestionsAdapter<?> adapter, long id) {
5887a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (DBG) Log.d(TAG, "query refine clicked, pos " + id);
5897a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
5907a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) {
5915f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
5925f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
5937a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        String query = suggestion.getSuggestionQuery();
5945f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (TextUtils.isEmpty(query)) {
5955f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
5965f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
5975f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
5985f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Log refine click
5997a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(),
600c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert                Logger.SUGGESTION_CLICK_TYPE_REFINE);
6015f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
6025f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Put query + space in query text view
6035f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        String queryWithSpace = query + ' ';
6045f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        setQuery(queryWithSpace, false);
605dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        updateSuggestions();
6067010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.focusQueryTextView();
6073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
6083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6094ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private void updateSuggestionsBuffered() {
6107f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        if (DBG) Log.d(TAG, "updateSuggestionsBuffered()");
611d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.removeCallbacks(mUpdateSuggestionsTask);
6124ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        long delay = getConfig().getTypingUpdateSuggestionsDelayMillis();
613d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.postDelayed(mUpdateSuggestionsTask, delay);
6144ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
6154ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
616b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private void gotSuggestions(Suggestions suggestions) {
61711234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (mStarting) {
61811234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            mStarting = false;
61911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
62011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            int latency = mStartLatencyTracker.getLatency();
62198cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood            getLogger().logStart(mOnCreateLatency, latency, source, getCorpus(),
622848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani                    suggestions == null ? null : suggestions.getExpectedCorpora());
623b5560dbe16a8fb3148b0fb24c73836bf2e84dd61Mathew Inwood            getQsbApplication().onStartupComplete();
62411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        }
625848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    }
626848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
6276859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert    private void getCorporaToQuery(Consumer<List<Corpus>> consumer) {
628dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        Corpus corpus = getCorpus();
629dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        if (corpus == null) {
630dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            getCorpusRanker().getCorporaInAll(Consumers.createAsyncConsumer(mHandler, consumer));
631dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        } else {
632dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            List<Corpus> corpora = new ArrayList<Corpus>();
633dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            Corpus searchCorpus = getSearchCorpus();
634dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            if (searchCorpus != null) corpora.add(searchCorpus);
635dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            consumer.consume(corpora);
636dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        }
6374572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood    }
6384572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood
639b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert    protected void getShortcutsForQuery(String query, Collection<Corpus> corporaToQuery,
640b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert            final Suggestions suggestions) {
641b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        ShortcutRepository shortcutRepo = getShortcutRepository();
642b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert        if (shortcutRepo == null) return;
643e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood        if (query.length() == 0 && !getConfig().showShortcutsForZeroQuery()) {
644e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood            return;
645e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood        }
646b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert        Consumer<ShortcutCursor> consumer = Consumers.createAsyncCloseableConsumer(mHandler,
647b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert                new Consumer<ShortcutCursor>() {
648b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert            public boolean consume(ShortcutCursor shortcuts) {
649b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert                suggestions.setShortcuts(shortcuts);
650b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert                return true;
651b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert            }
652b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert        });
6538749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert        shortcutRepo.getShortcutsForQuery(query, corporaToQuery,
6548749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert                getSettings().allowWebSearchShortcuts(), consumer);
655b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
65611234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert
657dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert    public void updateSuggestions() {
6587f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        if (DBG) Log.d(TAG, "updateSuggestions()");
6597f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        final String query = CharMatcher.WHITESPACE.trimLeadingFrom(getQuery());
6604572856ac85bb53ea06e65d00beebdf336af9b27Mathew Inwood        getQsbApplication().getSourceTaskExecutor().cancelPendingTasks();
6616859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert        getCorporaToQuery(new Consumer<List<Corpus>>(){
6626859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert            @Override
6636859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert            public boolean consume(List<Corpus> corporaToQuery) {
6646859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert                updateSuggestions(query, corporaToQuery);
6656859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert                return true;
6666859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert            }
6676859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert        });
6686859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert    }
669b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
6706859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert    protected void updateSuggestions(String query, List<Corpus> corporaToQuery) {
6717f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        if (DBG) Log.d(TAG, "updateSuggestions(\"" + query+"\"," + corporaToQuery + ")");
672b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        Suggestions suggestions = getSuggestionsProvider().getSuggestions(
673b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert                query, corporaToQuery);
674b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert        getShortcutsForQuery(query, corporaToQuery, suggestions);
675b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert
676b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        // Log start latency if this is the first suggestions update
677b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        gotSuggestions(suggestions);
678b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
6795880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        showSuggestions(suggestions);
6805880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    }
6815880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert
6825880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected void showSuggestions(Suggestions suggestions) {
6837010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSuggestions(suggestions);
684185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
685185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
686185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
687848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
6887a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionQuickContactClicked(SuggestionsAdapter<?> adapter, long id) {
6897a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            clickedQuickContact(adapter, id);
690848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        }
6913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6927a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionClicked(SuggestionsAdapter<?> adapter, long id) {
6937a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            launchSuggestion(adapter, id);
694848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        }
695145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
6967a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionRemoveFromHistoryClicked(SuggestionsAdapter<?> adapter, long id) {
6973bcf2de0b52d088a928f0efe962608baa9cc1e39Bjorn Bringert            removeFromHistoryClicked(adapter, id);
698848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        }
6993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
7007a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionQueryRefineClicked(SuggestionsAdapter<?> adapter, long id) {
7017a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            refineSuggestion(adapter, id);
7022617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
703f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    }
704f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
705dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    private class CorpusSelectorDismissListener implements DialogInterface.OnDismissListener {
706dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        public void onDismiss(DialogInterface dialog) {
707dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            if (DBG) Log.d(TAG, "Corpus selector dismissed");
708dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert            clearStartedIntoCorpusSelectionDialog();
709dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert        }
710dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert    }
711dfe0df4a730c54bdc963b891f9a3080c38055ebbBjorn Bringert
71221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private class CorporaObserver extends DataSetObserver {
71321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        @Override
71421bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        public void onChanged() {
71521bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert            setCorpus(getCorpusName());
716dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert            updateSuggestions();
71721bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        }
71821bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    }
71921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
7202607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    public interface OnDestroyListener {
7212607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        void onDestroyed();
7222607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    }
7232607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
7243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
725