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
193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
22fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.net.Uri;
233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
2411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport android.os.Debug;
254ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringertimport android.os.Handler;
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
30ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood
31ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.common.Search;
32ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.ui.SearchActivityView;
33ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.ui.SuggestionClickListener;
34ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.ui.SuggestionsAdapter;
35ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.google.common.annotations.VisibleForTesting;
36ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.google.common.base.CharMatcher;
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3811234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport java.io.File;
39ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
46754cb6b769c5955fc707a6c5ab6689b64df102b3Mathew Inwood    private static final boolean DBG = false;
470484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchActivity";
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
49fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String SCHEME_CORPUS = "qsb.corpus";
50fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
5148ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert    private static final String INTENT_EXTRA_TRACE_START_UP = "trace_start_up";
5248ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert
539ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
54839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    private static final String INSTANCE_KEY_QUERY = "query";
55713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
5671f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert    private static final String ACTIVITY_HELP_CONTEXT = "search";
5771f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert
5848ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert    private boolean mTraceStartUp;
59f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    // Measures time from for last onCreate()/onNewIntent() call.
60f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    private LatencyTracker mStartLatencyTracker;
6198cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    // Measures time spent inside onCreate()
6298cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private LatencyTracker mOnCreateTracker;
6398cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private int mOnCreateLatency;
64ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume().
65ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mStarting;
66ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // True if the user has taken some action, e.g. launching a search, voice search,
67ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    // or suggestions, since QSB was last started.
68ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private boolean mTookAction;
69ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private SearchActivityView mSearchActivityView;
71185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
72ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    private Source mSource;
7321bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
742617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert    private Bundle mAppSearchData;
753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
76d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Handler mHandler = new Handler();
77d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mUpdateSuggestionsTask = new Runnable() {
78ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
794ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        public void run() {
807f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert            updateSuggestions();
814ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        }
824ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    };
834ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
84d6b9bd42f22582a8ccec657bd6d715465de98ec9Mathew Inwood    private final Runnable mShowInputMethodTask = new Runnable() {
85ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
86d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        public void run() {
877010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mSearchActivityView.showInputMethodForQuery();
88d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
89d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    };
90d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
912607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    private OnDestroyListener mDestroyListener;
922607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
9648ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        mTraceStartUp = getIntent().hasExtra(INTENT_EXTRA_TRACE_START_UP);
9748ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        if (mTraceStartUp) {
9848ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            String traceFile = new File(getDir("traces", 0), "qsb-start.trace").getAbsolutePath();
9948ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            Log.i(TAG, "Writing start-up trace to " + traceFile);
10048ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert            Debug.startMethodTracing(traceFile);
10148ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        }
102ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
105713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
106c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        // This forces the HTTP request to check the users domain to be
107c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        // sent as early as possible.
108c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        QsbApplication.get(this).getSearchBaseUrlHelper();
109c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
110ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        mSource = QsbApplication.get(this).getGoogleSource();
111ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood
1125880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        mSearchActivityView = setupContentView();
113aa7d79792baca59eb7afe00ea27abc5176ddd34bMathew Inwood
114f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        if (getConfig().showScrollingResults()) {
1157a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            mSearchActivityView.setMaxPromotedResults(getConfig().getMaxPromotedResults());
116f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        } else {
117f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood            mSearchActivityView.limitResultsToViewHeight();
118f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood        }
119848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
1207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSearchClickListener(new SearchActivityView.SearchClickListener() {
121ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            @Override
1227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public boolean onSearchClicked(int method) {
1237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return SearchActivity.this.onSearchClicked(method);
1247010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1257010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
1262617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
1277010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setQueryListener(new SearchActivityView.QueryListener() {
128ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            @Override
1297010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public void onQueryChanged() {
1307010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                updateSuggestionsBuffered();
1317010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1327010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
133782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
1347010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSuggestionClickListener(new ClickHandler());
1357be9c66fdc2e4df2e998d79a11bcf737ffddc2dcBjorn Bringert
1367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setVoiceSearchButtonClickListener(new View.OnClickListener() {
137ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            @Override
1387010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public void onClick(View view) {
1397010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                onVoiceSearchClicked();
1407010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
1429ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
143e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        View.OnClickListener finishOnClick = new View.OnClickListener() {
144ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            @Override
1459e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            public void onClick(View v) {
1469e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood                finish();
1479e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            }
148e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        };
149e833f84eee7ab575c8d53dec9b133fa5de9c7e6dMathew Inwood        mSearchActivityView.setExitClickListener(finishOnClick);
1509e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood
1519ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1529ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1539ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1549ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1559ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1569ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
157713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
158713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
1597010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.start();
16021bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert
16198cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        recordOnCreateDone();
1629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
163713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1645880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected SearchActivityView setupContentView() {
1656ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert        setContentView(R.layout.search_activity);
1665880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        return (SearchActivityView) findViewById(R.id.search_activity_view);
1675880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    }
1685880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert
1695880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected SearchActivityView getSearchActivityView() {
1705880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        return mSearchActivityView;
1716ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert    }
1726ae65b29ce9313a4f0624ef825f75151db5ec2feBjorn Bringert
1739ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1749ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
175839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onNewIntent()");
176ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        recordStartTime();
1779ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1789ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1799ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
181ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private void recordStartTime() {
182f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        mStartLatencyTracker = new LatencyTracker();
18398cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        mOnCreateTracker = new LatencyTracker();
184ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mStarting = true;
185ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = false;
186ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
187ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
18898cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    private void recordOnCreateDone() {
18998cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood        mOnCreateLatency = mOnCreateTracker.getLatency();
19098cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood    }
19198cbee7f6266509a0805b3fef060f01caaef69e3Mathew Inwood
1929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
1939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
194839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_QUERY);
195839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, false);
196713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
197713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
198713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
1999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
2009ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
2019ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
2029ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
203fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
204839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        outState.putString(INSTANCE_KEY_QUERY, getQuery());
205713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
206713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
207713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
208713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
20921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        String corpusName = getCorpusNameFromUri(intent.getData());
2102617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        String query = intent.getStringExtra(SearchManager.QUERY);
2112617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
212839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        boolean selectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false);
2132617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
214839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        setQuery(query, selectAll);
215c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        mAppSearchData = appSearchData;
2162617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert
217fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
218fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
21921bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert    private String getCorpusNameFromUri(Uri uri) {
220fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (uri == null) return null;
221fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (!SCHEME_CORPUS.equals(uri.getScheme())) return null;
22221bff9bbf4286907b01d3153bff2fbd6b5ec5df8Bjorn Bringert        return uri.getAuthority();
223fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
224fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
2253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
22649fd8e0994577badc6194c2c3b5f771f2b793fe4Bjorn Bringert        return QsbApplication.get(this);
2273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2294ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private Config getConfig() {
2304ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        return getQsbApplication().getConfig();
2314ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
2324ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
23396fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    protected SearchSettings getSettings() {
23496fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert        return getQsbApplication().getSettings();
23596fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    }
23696fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert
237b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
238b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        return getQsbApplication().getSuggestionsProvider();
23947d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert    }
24047d02f7285794bb39b2a2d828d32b5329dd8ecb0Bjorn Bringert
241ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    private Logger getLogger() {
242ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return getQsbApplication().getLogger();
243ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
244ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
2452607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    @VisibleForTesting
2462607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    public void setOnDestroyListener(OnDestroyListener l) {
2472607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        mDestroyListener = l;
2482607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    }
2492607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
2503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
2523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
2537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.destroy();
2542607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        super.onDestroy();
2552607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        if (mDestroyListener != null) {
2562607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood            mDestroyListener.onDestroyed();
2572607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        }
2583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
2629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
263f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        if (!mTookAction) {
264848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani            // TODO: This gets logged when starting other activities, e.g. by opening the search
265f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            // settings, or clicking a notification in the status bar.
266aa7d79792baca59eb7afe00ea27abc5176ddd34bMathew Inwood            // TODO we should log both sets of suggestions in 2-pane mode
267f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert            getLogger().logExit(getCurrentSuggestions(), getQuery().length());
268f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert        }
2699ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
2703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
2717010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.clearSuggestions();
2720a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchActivityView.onStop();
2733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
2743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2772e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    protected void onPause() {
2782e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        if (DBG) Log.d(TAG, "onPause()");
2792e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        mSearchActivityView.onPause();
2802e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        super.onPause();
2812e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    }
2822e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani
2832e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    @Override
284839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    protected void onRestart() {
285839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        if (DBG) Log.d(TAG, "onRestart()");
286839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert        super.onRestart();
287839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    }
288839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert
289839a9fd2828f37c9dc8345f93aefa5b8ad2f857fBjorn Bringert    @Override
290b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
2919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
292b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
29311234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        updateSuggestionsBuffered();
2940a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchActivityView.onResume();
29548ced2f683491d07d892ec81f88fe2e26f9207c2Bjorn Bringert        if (mTraceStartUp) Debug.stopMethodTracing();
296b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
297b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
298b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
29973a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert    public boolean onPrepareOptionsMenu(Menu menu) {
30073a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        // Since the menu items are dynamic, we recreate the menu every time.
30173a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        menu.clear();
30273a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert        createMenuItems(menu, true);
30396fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert        return true;
30496fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    }
30596fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert
30673a375928e0b8f0b2bfa09e4b252cfcbdad4ef84Bjorn Bringert    public void createMenuItems(Menu menu, boolean showDisabled) {
30771f9f8ccd3c12d7d48e2fee4c55cd57e1970b797Bjorn Bringert        getQsbApplication().getHelp().addHelpMenuItem(menu, ACTIVITY_HELP_CONTEXT);
3085eee22a4ec6fc45deb9706ba535039ccae51b90aBjorn Bringert    }
3095eee22a4ec6fc45deb9706ba535039ccae51b90aBjorn Bringert
31096fec862c3d494aebcb4e1d93589a241385a2ba7Bjorn Bringert    @Override
311d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    public void onWindowFocusChanged(boolean hasFocus) {
312d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        super.onWindowFocusChanged(hasFocus);
313d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        if (hasFocus) {
314d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            // Launch the IME after a bit
315d98911178013162737fbba74387b51d2a08b0493Amith Yamasani            mHandler.postDelayed(mShowInputMethodTask, 0);
316d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        }
317d98911178013162737fbba74387b51d2a08b0493Amith Yamasani    }
318d98911178013162737fbba74387b51d2a08b0493Amith Yamasani
319713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return mSearchActivityView.getQuery();
3213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void setQuery(String query, boolean selectAll) {
3247010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setQuery(query, selectAll);
325e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert    }
326e4f5c630a1c9cdf75fe751dc728729c3ecb7ae07Bjorn Bringert
327dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    /**
328dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     * @return true if a search was performed as a result of this click, false otherwise.
329dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood     */
330dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood    protected boolean onSearchClicked(int method) {
3313a7125b39b72f7417684c4d3040abeb4a81bd6b3Bjorn Bringert        String query = CharMatcher.WHITESPACE.trimAndCollapseFrom(getQuery(), ' ');
3323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
333eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
334eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert        // Don't do empty queries
335dd6d9a1a8d559c87f54412eb4e6569ed62193d60Mathew Inwood        if (TextUtils.getTrimmedLength(query) == 0) return false;
336eba26da75665ac3b9a411e74267395f332ff851aBjorn Bringert
337ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
338c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
339c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log search start
340ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        getLogger().logSearch(method, query.length());
341c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
342c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start search
343ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        startSearch(mSource, query);
3449d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert        return true;
3459d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    }
3469d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
347ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    protected void startSearch(Source searchSource, String query) {
348ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        Intent intent = searchSource.createSearchIntent(query, mAppSearchData);
34981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
354c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert
355535931433926d342c6277034cad91143ae28b72dBjorn Bringert        mTookAction = true;
356535931433926d342c6277034cad91143ae28b72dBjorn Bringert
357c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log voice search start
358ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        getLogger().logVoiceSearch();
359ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
360c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Start voice search
361ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        Intent intent = mSource.createVoiceSearchIntent(mAppSearchData);
36281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
36381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
36481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
365ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    protected Source getSearchSource() {
366ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mSource;
367014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert    }
368014e0d0c0a5102b7cc1c5576a3af25a646731dd0Bjorn Bringert
369e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert    protected SuggestionCursor getCurrentSuggestions() {
370ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mSearchActivityView.getSuggestions().getResult();
371782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
372782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
3737a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected SuggestionPosition getCurrentSuggestions(SuggestionsAdapter<?> adapter, long id) {
3747a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition pos = adapter.getSuggestion(id);
3757a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (pos == null) {
3767a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            return null;
3777a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        }
3787a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionCursor suggestions = pos.getCursor();
3797a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        int position = pos.getPosition();
3805f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (suggestions == null) {
3815f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
3825f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
3835f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        int count = suggestions.getCount();
3845f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (position < 0 || position >= count) {
3855f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            Log.w(TAG, "Invalid suggestion position " + position + ", count = " + count);
3865f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return null;
3875f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
3885f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        suggestions.moveTo(position);
3897a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        return pos;
3905f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert    }
3915f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
39281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    protected void launchIntent(Intent intent) {
39313b4f2dc4b339790c2b9b0220be47c8e77fd61eaMathew Inwood        if (DBG) Log.d(TAG, "launchIntent " + intent);
39481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (intent == null) {
39581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            return;
39681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
39781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        try {
39881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            startActivity(intent);
39981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        } catch (RuntimeException ex) {
40081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // Since the intents for suggestions specified by suggestion providers,
40181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            // guard against them not being handled, not allowed, etc.
40281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            Log.e(TAG, "Failed to start " + intent.toUri(0), ex);
40381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
40481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
40581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
4067a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    private boolean launchSuggestion(SuggestionsAdapter<?> adapter, long id) {
4077a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
4087a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) return false;
4096f8fc42e68237bfb6f474faff8086d910d2934d5Bjorn Bringert
4107a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (DBG) Log.d(TAG, "Launching suggestion " + id);
411ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        mTookAction = true;
412e06b7cbf55301a24cfd7525a91107e3cd2c9f48eBjorn Bringert
413c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Log suggestion click
414ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        getLogger().logSuggestionClick(id, suggestion.getCursor(),
415c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert                Logger.SUGGESTION_CLICK_TYPE_LAUNCH);
416ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
417c29c9f854db8fa0c85f17cc32bae33dc17c6b127Bjorn Bringert        // Launch intent
4187a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        launchSuggestion(suggestion.getCursor(), suggestion.getPosition());
4199d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
4209d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert        return true;
4219d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    }
4229d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert
4239d731c0bae275ee9b1c87b679820259c18cc68c8Bjorn Bringert    protected void launchSuggestion(SuggestionCursor suggestions, int position) {
42481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        suggestions.moveTo(position);
42593bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        Intent intent = SuggestionUtils.getSuggestionIntent(suggestions, mAppSearchData);
42681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        launchIntent(intent);
4273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4297a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected void refineSuggestion(SuggestionsAdapter<?> adapter, long id) {
4307a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (DBG) Log.d(TAG, "query refine clicked, pos " + id);
4317a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        SuggestionPosition suggestion = getCurrentSuggestions(adapter, id);
4327a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        if (suggestion == null) {
4335f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
4345f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
4357a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        String query = suggestion.getSuggestionQuery();
4365f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        if (TextUtils.isEmpty(query)) {
4375f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert            return;
4385f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        }
4395f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
4405f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Log refine click
441ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        getLogger().logSuggestionClick(id, suggestion.getCursor(),
442c020c1844b0fb3a825e8a6fa6ad96288bc432fbcBjorn Bringert                Logger.SUGGESTION_CLICK_TYPE_REFINE);
4435f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert
4445f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        // Put query + space in query text view
4455f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        String queryWithSpace = query + ' ';
4465f71d5746ec042ad2e5c1eee8c83514f92534372Bjorn Bringert        setQuery(queryWithSpace, false);
447dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert        updateSuggestions();
4487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.focusQueryTextView();
4493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4514ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    private void updateSuggestionsBuffered() {
4527f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        if (DBG) Log.d(TAG, "updateSuggestionsBuffered()");
453d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.removeCallbacks(mUpdateSuggestionsTask);
4544ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert        long delay = getConfig().getTypingUpdateSuggestionsDelayMillis();
455d98911178013162737fbba74387b51d2a08b0493Amith Yamasani        mHandler.postDelayed(mUpdateSuggestionsTask, delay);
4564ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert    }
4574ef1338a23b040df2ef180c48ff85e14a9d70906Bjorn Bringert
458b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private void gotSuggestions(Suggestions suggestions) {
45911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        if (mStarting) {
46011234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            mStarting = false;
46111234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            String source = getIntent().getStringExtra(Search.SOURCE);
46211234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert            int latency = mStartLatencyTracker.getLatency();
463ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            getLogger().logStart(mOnCreateLatency, latency, source);
464b5560dbe16a8fb3148b0fb24c73836bf2e84dd61Mathew Inwood            getQsbApplication().onStartupComplete();
46511234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert        }
466848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani    }
467848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
468dfc1772caf35942837d83331d787eb10734c37cbBjorn Bringert    public void updateSuggestions() {
4697f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        if (DBG) Log.d(TAG, "updateSuggestions()");
4707f5ff91319a8433abd92f3e3179158e38391e159Bjorn Bringert        final String query = CharMatcher.WHITESPACE.trimLeadingFrom(getQuery());
471ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        updateSuggestions(query, mSource);
4726859aead3af0680b2c9dc326244aa89835c2c852Bjorn Bringert    }
473b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
474ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    protected void updateSuggestions(String query, Source source) {
475ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        if (DBG) Log.d(TAG, "updateSuggestions(\"" + query+"\"," + source + ")");
476b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        Suggestions suggestions = getSuggestionsProvider().getSuggestions(
477ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood                query, source);
478b42184f1e6a1b7bb22797ff92cae696753aca770Bjorn Bringert
479b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        // Log start latency if this is the first suggestions update
480b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        gotSuggestions(suggestions);
481b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
4825880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert        showSuggestions(suggestions);
4835880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    }
4845880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert
4855880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert    protected void showSuggestions(Suggestions suggestions) {
4867010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchActivityView.setSuggestions(suggestions);
487185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
488185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
489185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
490848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani
491ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4927a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionClicked(SuggestionsAdapter<?> adapter, long id) {
4937a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            launchSuggestion(adapter, id);
494848fa7a19abedc372452073abaf52780c7b6d78dAmith Yamasani        }
495145693e12b77c193a65b7eaa038a272dd1f48f33Bjorn Bringert
496ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4977a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood        public void onSuggestionQueryRefineClicked(SuggestionsAdapter<?> adapter, long id) {
4987a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood            refineSuggestion(adapter, id);
4992617a0177a6088d5aaf381263229bf5a62d2238dBjorn Bringert        }
500f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert    }
501f95ce100dcbc77794b79b0187c566bb58b5978d3Bjorn Bringert
5022607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    public interface OnDestroyListener {
5032607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood        void onDestroyed();
5042607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood    }
5052607687d72deb8e06329597ab8bffcca9c746153Mathew Inwood
5063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
507