SearchActivity.java revision 9ad03a750a66b26441a19ff54b6057729c145eae
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
199ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringertimport com.android.quicksearchbox.ui.SearchSourceSelector;
20185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionClickListener;
21185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionViewFactory;
22185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsAdapter;
23185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringertimport com.android.quicksearchbox.ui.SuggestionsView;
24185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.Activity;
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
27713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringertimport android.content.ComponentName;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.DataSetObserver;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.Rect;
313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.drawable.Animatable;
32d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinneyimport android.graphics.drawable.Drawable;
333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.Editable;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextWatcher;
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.KeyEvent;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.Menu;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View;
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.View.OnFocusChangeListener;
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.EditText;
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.widget.ImageButton;
453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
46782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: restore user query when using dpad to move up from top suggestion
47782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: nicer progress animation. one per source?
48782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: handle long clicks
49782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: support action keys
50782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: support IME search action
51782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: allow typing everywhere in the UI
52185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: don't show new results until there is at least one, or it's done
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: add timeout for source queries
553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: group refreshes that happen close to each other.
563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: use queryAfterZeroResults()
57782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
58782dd228e78e9294692d639597f96c26283968bbBjorn Bringert// TODO: use permission to get extended Genie suggestions
593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: support intent extras for source (e.g. launcher widget)
603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: support intent extras for initial state, e.g. query, selection
613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: make Config server-side configurable
623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: log impressions
633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert// TODO: use source ranking
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * The main activity for Quick Search Box. Shows the search UI.
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchActivity extends Activity {
703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final boolean DBG = true;
723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final String TAG = "SearchActivity";
733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    // TODO: This is hidden in SearchManager
753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public final static String INTENT_ACTION_SEARCH_SETTINGS
763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            = "android.search.action.SEARCH_SETTINGS";
773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
789ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    public static final String EXTRA_KEY_SEARCH_SOURCE
799ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            = "search_source";
809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
819ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    // Keys for the saved instance state.
829ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private static final String INSTANCE_KEY_SOURCE = "source";
839ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private static final String INSTANCE_KEY_USER_QUERY = "query";
84713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
85185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    protected SuggestionsAdapter mSuggestionsAdapter;
86185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected EditText mQueryTextView;
883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected SuggestionsView mSuggestionsView;
903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mSearchGoButton;
923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ImageButton mVoiceSearchButton;
939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected SearchSourceSelector mSourceSelector;
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private Launcher mLauncher;
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
979ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private ComponentName mSourceName;
98713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mUpdateSuggestions;
99713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private String mUserQuery;
100713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private boolean mSelectAll;
1013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Called when the activity is first created. */
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void onCreate(Bundle savedInstanceState) {
1053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onCreate()");
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreate(savedInstanceState);
107713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setContentView(R.layout.search_bar);
1093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
110d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        Config config = getConfig();
111185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        SuggestionViewFactory viewFactory = getSuggestionViewFactory();
112185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter = new SuggestionsAdapter(viewFactory);
113185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter
114185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert                .setInitialSourceResultWaitMillis(config.getInitialSourceResultWaitMillis());
115185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter
116185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert                .setSourceResultPublishDelayMillis(config.getSourceResultPublishDelayMillis());
117185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView = (EditText) findViewById(R.id.search_src_text);
1193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
120782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setSuggestionClickListener(new ClickHandler());
121782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSuggestionsView.setInteractionListener(new InputMethodCloser());
122713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
1269ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceSelector = new SearchSourceSelector(findViewById(R.id.search_source_selector));
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1289ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mLauncher = new Launcher(this);
1293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mVoiceSearchButton.setVisibility(
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                mLauncher.isVoiceSearchAvailable() ? View.VISIBLE : View.GONE);
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
132782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
133782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
134782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mQueryTextView.setOnFocusChangeListener(new SuggestListFocusListener());
135782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
136782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
137782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mSearchGoButton.setOnKeyListener(new ButtonsKeyListener());
138782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
139782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
140782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mVoiceSearchButton.setOnKeyListener(new ButtonsKeyListener());
141713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1429ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUpdateSuggestions = true;
1439ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1449ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // First get setup from intent
1459ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Intent intent = getIntent();
1469ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1479ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Then restore any saved instance state
1489ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        restoreInstanceState(savedInstanceState);
1499ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
150713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // Do this at the end, to avoid updating the list view when setSource()
151713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        // is called.
152713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        mSuggestionsView.setAdapter(mSuggestionsAdapter);
1539ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
154713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1559ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    @Override
1569ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onNewIntent(Intent intent) {
1579ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setIntent(intent);
1589ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setupFromIntent(intent);
1599ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1609ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1619ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void restoreInstanceState(Bundle savedInstanceState) {
1629ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (savedInstanceState == null) return;
1639ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        ComponentName sourceName = savedInstanceState.getParcelable(INSTANCE_KEY_SOURCE);
1649ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        String query = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
1659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setSource(getSourceByComponentName(sourceName));
1669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setUserQuery(query);
167713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
168713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
169713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    @Override
1709ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void onSaveInstanceState(Bundle outState) {
1719ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        super.onSaveInstanceState(outState);
1729ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // We don't save appSearchData, since we always get the value
1739ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // from the intent and the user can't change it.
1749ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        outState.putParcelable(INSTANCE_KEY_SOURCE, mSourceName);
1759ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        outState.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
176713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
177713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
178713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private void setupFromIntent(Intent intent) {
179713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
1809ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Source source = getSourceByName(intent.getStringExtra(EXTRA_KEY_SEARCH_SOURCE));
1819ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setSource(source);
1829ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // TODO: Should this be SearchManager.INITIAL_QUERY?
1839ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setUserQuery(intent.getStringExtra(SearchManager.QUERY));
1849ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // TODO: Expose SearchManager.SELECT_INITIAL_QUERY
1859ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSelectAll = false;
1869ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setAppSearchData(intent.getBundleExtra(SearchManager.APP_DATA));
1879ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
1889ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
1899ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private Source getSourceByName(String sourceNameStr) {
1909ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (sourceNameStr == null) return null;
1919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        ComponentName sourceName = ComponentName.unflattenFromString(sourceNameStr);
1929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (sourceName == null) {
1939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            Log.w(TAG, "Malformed source name: " + sourceName);
1949ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
1959ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        }
1969ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        return getSourceByComponentName(sourceName);
1979ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
198713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
1999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private Source getSourceByComponentName(ComponentName sourceName) {
2009ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Source source = getSources().getSourceByComponentName(sourceName);
2019ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (source == null) {
2029ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            Log.w(TAG, "Unknown source " + sourceName);
2039ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            return null;
204713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
2059ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        return source;
206713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
207713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
2089ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    private void setSource(Source source) {
2099ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceName = source == null ? null : source.getComponentName();
2109ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        Drawable sourceIcon;
2119ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (source == null) {
2129ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            sourceIcon = getSuggestionViewFactory().getGlobalSearchIcon();
213713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        } else {
2149ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert            sourceIcon = source.getSourceIcon();
215713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        }
2169ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSuggestionsAdapter.setSource(mSourceName);
2179ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceSelector.setSource(mSourceName);
2189ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceSelector.setSourceIcon(sourceIcon);
2193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private QsbApplication getQsbApplication() {
2223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (QsbApplication) getApplication();
2233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
225d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney    private Config getConfig() {
226d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        return getQsbApplication().getConfig();
227d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney    }
228d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney
229713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private SourceLookup getSources() {
230713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert        return getQsbApplication().getSources();
231713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    }
232713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
2333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private ShortcutRepository getShortcutRepository() {
2343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getShortcutRepository();
2353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private SuggestionsProvider getSuggestionsProvider() {
2383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getQsbApplication().getSuggestionsProvider();
2393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
241185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private SuggestionViewFactory getSuggestionViewFactory() {
242185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        return getQsbApplication().getSuggestionViewFactory();
243185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
244185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
2453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onDestroy() {
2473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "onDestroy()");
2483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onDestroy();
249185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsView.setAdapter(null);  // closes mSuggestionsAdapter
2503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
2533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onStop() {
2549ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onStop()");
2559ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Close all open suggestion cursors. The query will be redone in onResume()
2563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if we come back to this activity.
257185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
2583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onStop();
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
262b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    protected void onResume() {
2639ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (DBG) Log.d(TAG, "onResume()");
264b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        super.onResume();
2659ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        setQuery(mUserQuery, mSelectAll);
2669ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        // Only select everything the first time after creating the activity.
2679ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSelectAll = false;
2689ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        updateSuggestions(mUserQuery);
269b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert        mQueryTextView.requestFocus();
270b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    }
271b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert
272b32ee8f1b45c65daff18b40c0614cf18843b8c17Bjorn Bringert    @Override
2733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean onCreateOptionsMenu(Menu menu) {
2743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super.onCreateOptionsMenu(menu);
2753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Intent settings = new Intent(INTENT_ACTION_SEARCH_SETTINGS);
2773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        settings.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
2783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // Don't show activity chooser if there are multiple search settings activities,
2793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // e.g. from different QSB implementations.
2803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        settings.setPackage(this.getPackageName());
2813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        menu.add(Menu.NONE, Menu.NONE, 0, R.string.menu_settings)
2823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
2833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .setIntent(settings);
2843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
2863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2889ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    /**
2899ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * Sets the query as typed by the user. Does not update the suggestions
2909ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     * or the text in the query box.
2919ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert     */
2929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setUserQuery(String userQuery) {
2939ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        if (userQuery == null) userQuery = "";
2949ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mUserQuery = userQuery;
2959ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceSelector.setQuery(mUserQuery);
2969ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
2979ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
2989ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    protected void setAppSearchData(Bundle appSearchData) {
2999ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mLauncher.setAppSearchData(appSearchData);
3009ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert        mSourceSelector.setAppSearchData(appSearchData);
3019ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert    }
3029ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert
303713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    protected String getQuery() {
3043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        CharSequence q = mQueryTextView.getText();
3053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return q == null ? "" : q.toString();
3063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Restores the query entered by the user.
3103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void restoreUserQuery() {
3123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Restoring query to '" + mUserQuery + "'");
3133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setQuery(mUserQuery, false);
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions,
3183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * and does not change the saved user-entered query.
3193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * {@link #restoreUserQuery()} will restore the query to the last
3203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * user-entered query.
3213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setQuery(String query, boolean selectAll) {
3233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = false;
3243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mQueryTextView.setText(query);
3253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        setTextSelection(selectAll);
3263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mUpdateSuggestions = true;
3273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Sets the text selection in the query text view.
3313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param selectAll If {@code true}, selects the entire query.
3333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        If {@false}, no characters are selected, and the cursor is placed
3343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        at the end of the query.
3353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void setTextSelection(boolean selectAll) {
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (selectAll) {
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(0, mQueryTextView.length());
3393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
3403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mQueryTextView.setSelection(mQueryTextView.length());
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onSearchClicked() {
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getQuery();
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Search clicked, query=" + query);
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mLauncher.startWebSearch(query);
3483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onVoiceSearchClicked() {
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Voice Search clicked");
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mLauncher.startVoiceSearch();
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
355782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected boolean launchSuggestion(SuggestionPosition suggestion) {
356782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return launchSuggestion(suggestion, KeyEvent.KEYCODE_UNKNOWN, null);
357782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
358782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
359782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected boolean launchSuggestion(SuggestionPosition suggestion,
360782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            int actionKey, String actionMsg) {
361782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (DBG) Log.d(TAG, "Launching suggestion " + suggestion);
362782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        mLauncher.launchSuggestion(suggestion, actionKey, actionMsg);
3633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        getShortcutRepository().reportClick(suggestion);
3643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // Update search widgets, since the top shortcuts can have changed.
3653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        SearchWidgetProvider.updateSearchWidgets(this);
3663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
3673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
369782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected boolean launchSuggestionSecondary(SuggestionPosition suggestion, Rect target) {
3703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert      if (DBG) Log.d(TAG, "Clicked on suggestion icon " + suggestion);
3713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert      mLauncher.launchSuggestionSecondary(suggestion, target);
3723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert      getShortcutRepository().reportClick(suggestion);
3733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert      return true;
3743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
376782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected boolean onSuggestionLongClicked(SuggestionPosition suggestion) {
377782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        SuggestionCursor sourceResult = suggestion.getSuggestion();
3783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "Long clicked on suggestion " + sourceResult.getSuggestionText1());
3793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return false;
3803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
382782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected void onSuggestionSelected(SuggestionPosition suggestion) {
383782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        SuggestionCursor sourceResult = suggestion.getSuggestion();
3843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String displayQuery = sourceResult.getSuggestionDisplayQuery();
3853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) {
3863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.d(TAG, "Selected suggestion " + sourceResult.getSuggestionText1()
3873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    + ",displayQuery="+ displayQuery);
3883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (TextUtils.isEmpty(displayQuery)) {
3903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            restoreUserQuery();
3913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
3923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            setQuery(displayQuery, false);
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
396782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected boolean onSuggestionKeyDown(SuggestionPosition suggestion,
397782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            int keyCode, KeyEvent event) {
398782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Treat enter or search as a click
399782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
400782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            return launchSuggestion(suggestion);
401782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
402782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
403782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // Handle source-specified action keys
404782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        String actionMsg = suggestion.getSuggestion().getActionKeyMsg(keyCode);
405782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (actionMsg != null) {
406782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            return launchSuggestion(suggestion, keyCode, actionMsg);
407782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
408782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
409782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return false;
410782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
411782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void onSourceSelected() {
4133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "No suggestion selected");
4143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        restoreUserQuery();
4153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
417782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected int getSelectedPosition() {
418782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedPosition();
419782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
420782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
421782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    protected SuggestionPosition getSelectedSuggestion() {
422782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return mSuggestionsView.getSelectedSuggestion();
423782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
424782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
4253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method.
4273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void hideInputMethod() {
4293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
4303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (imm != null) {
4313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            imm.hideSoftInputFromWindow(mQueryTextView.getWindowToken(), 0);
4323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Hides the input method when the suggestions get focus.
4373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
4393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
4403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (v == mQueryTextView) {
4413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                if (!focused) {
4423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    hideInputMethod();
4433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                } else {
4443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    // TODO: clear list selection?
4453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                }
4463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
4473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void startSearchProgress() {
4513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: Cache animation between calls?
4523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton.setImageResource(R.drawable.searching);
4533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Animatable animation = (Animatable) mSearchGoButton.getDrawable();
4543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        animation.start();
4553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void stopSearchProgress() {
458d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        Drawable animation = mSearchGoButton.getDrawable();
459d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        if (animation instanceof Animatable) {
460d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            // TODO: Is this needed, or is it done automatically when the
461d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            // animation is removed?
462d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            ((Animatable) animation).stop();
463d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        }
4643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchGoButton.setImageResource(R.drawable.ic_btn_search);
4653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private void updateSuggestions(String query) {
4683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        LatencyTracker latency = new LatencyTracker(TAG);
4693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Suggestions suggestions = getSuggestionsProvider().getSuggestions(query);
4703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        latency.addEvent("getSuggestions_done");
4713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (!suggestions.isDone()) {
4723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            suggestions.registerDataSetObserver(new ProgressUpdater(suggestions));
4733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            startSearchProgress();
474d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney        } else {
475d7a95801d20a9405b4ab9c9b4beecd106e880166Bryan Mawhinney            stopSearchProgress();
4763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
477185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
4783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        latency.addEvent("shortcuts_shown");
4793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        long userVisibleLatency = latency.getUserVisibleLatency();
4803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) {
4813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.d(TAG, "User visible latency (shortcuts): " + userVisibleLatency + " ms.");
4823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
4863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Filters the suggestions list when the search text changes.
4873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
4883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
4893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void afterTextChanged(Editable s) {
4903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mUpdateSuggestions) {
4913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                String query = s == null ? "" : s.toString();
4929ad03a750a66b26441a19ff54b6057729c145eaeBjorn Bringert                setUserQuery(query);
4933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                updateSuggestions(query);
4943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
4953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
4983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
4993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
5013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Handles non-text keys in the query text view.
5063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class QueryTextViewKeyListener implements View.OnKeyListener {
5083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public boolean onKey(View view, int keyCode, KeyEvent event) {
5093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Handle IME search action key
5103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
5113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                onSearchClicked();
5123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
5133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return false;
5143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
517782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
518782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the search and voice search buttons,
519782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * by refocusing to EditText.
520782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
521782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
522782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
523782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (!event.isSystem() &&
524782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
525782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
526782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
527782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
528782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                if (mQueryTextView.requestFocus()) {
529782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    return mQueryTextView.dispatchKeyEvent(event);
530782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
531782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
532782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            return false;
533782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
534782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
535782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
536782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    /**
537782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     * Handles key events on the suggestions list view.
538782dd228e78e9294692d639597f96c26283968bbBjorn Bringert     */
539713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert    private class SuggestionsViewKeyListener implements View.OnKeyListener {
540782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
541782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN) {
542782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                SuggestionPosition suggestion = getSelectedSuggestion();
543782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                if (suggestion != null) {
544782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                    return onSuggestionKeyDown(suggestion, keyCode, event);
545782dd228e78e9294692d639597f96c26283968bbBjorn Bringert                }
546782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            }
547782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            return false;
548782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
549782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
550782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
551185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class InputMethodCloser implements SuggestionsView.InteractionListener {
552185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        public void onInteraction() {
553185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            hideInputMethod();
554185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        }
555185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    }
556185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert
557185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert    private class ClickHandler implements SuggestionClickListener {
558713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       public void onSuggestionClicked(SuggestionPosition suggestion) {
559713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert           launchSuggestion(suggestion);
5603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
5613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
562713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       public boolean onSuggestionLongClicked(SuggestionPosition suggestion) {
563713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert           return SearchActivity.this.onSuggestionLongClicked(suggestion);
564713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       }
565713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
566713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       public void onSuggestionSelected(SuggestionPosition suggestion) {
567713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert           SearchActivity.this.onSuggestionSelected(suggestion);
568782dd228e78e9294692d639597f96c26283968bbBjorn Bringert       }
569782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
570713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert       public void onSuggestionIconClicked(SuggestionPosition suggestion, Rect rect) {
571713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert           launchSuggestionSecondary(suggestion, rect);
5723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
5733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the search button.
5773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
5793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
5803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onSearchClicked();
5813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Listens for clicks on the voice search button.
5863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class VoiceSearchButtonClickListener implements View.OnClickListener {
5883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onClick(View view) {
5893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            onVoiceSearchClicked();
5903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
5913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
5923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
5943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Updates the progress bar when the suggestions adapter changes its progress.
5953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
5963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private class ProgressUpdater extends DataSetObserver {
5973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        private final Suggestions mSuggestions;
5983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public ProgressUpdater(Suggestions suggestions) {
6003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mSuggestions = suggestions;
6013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
6033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        @Override
6043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public void onChanged() {
6053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (mSuggestions.isDone()) {
6063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                stopSearchProgress();
6073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
6083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
6093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
610713194910648268c094fa81b81f40ce2f7e39333Bjorn Bringert
6113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
612