1c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert/*
2c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * Copyright (C) 2010 The Android Open Source Project
3c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert *
4c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
5c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * you may not use this file except in compliance with the License.
6c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * You may obtain a copy of the License at
7c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert *
8c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
9c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert *
10c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
11c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
12c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * See the License for the specific language governing permissions and
14c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert * limitations under the License.
15c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert */
16c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
17c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringertpackage com.android.quicksearchbox.ui;
18c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
19c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringertimport android.content.Context;
207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.database.DataSetObserver;
217010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.graphics.drawable.Drawable;
227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.text.Editable;
239e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwoodimport android.text.TextUtils;
247010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.text.TextWatcher;
25c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringertimport android.util.AttributeSet;
267010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.util.Log;
27c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringertimport android.view.KeyEvent;
287010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.view.View;
297010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.view.inputmethod.CompletionInfo;
30c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringertimport android.view.inputmethod.InputMethodManager;
317010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.widget.AbsListView;
327010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport android.widget.ImageButton;
33fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwoodimport android.widget.ListAdapter;
3411234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringertimport android.widget.RelativeLayout;
35748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwoodimport android.widget.TextView;
36748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwoodimport android.widget.TextView.OnEditorActionListener;
37c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
38ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.Logger;
39ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.QsbApplication;
40ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.R;
41ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.SearchActivity;
42ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.SourceResult;
43ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.SuggestionCursor;
44ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.Suggestions;
45ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwoodimport com.android.quicksearchbox.VoiceSearch;
46ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood
477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport java.util.ArrayList;
487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringertimport java.util.Arrays;
497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5077909685887bd6db7454b73cf274afc3aca2f58dBjorn Bringertpublic abstract class SearchActivityView extends RelativeLayout {
510a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected static final boolean DBG = false;
520a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected static final String TAG = "QSB.SearchActivityView";
537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
547010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // The string used for privateImeOptions to identify to the IME that it should not show
557010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // a microphone button since one already exists in the search dialog.
567010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // TODO: This should move to android-common or something.
577010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private static final String IME_OPTION_NO_MICROPHONE = "nm";
587010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
590a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected QueryTextView mQueryTextView;
607010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // True if the query was empty on the previous call to updateQuery()
610a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected boolean mQueryWasEmpty = true;
620a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected Drawable mQueryTextEmptyBg;
630a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected Drawable mQueryTextNotEmptyBg;
640a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood
65fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood    protected SuggestionsListView<ListAdapter> mSuggestionsView;
66fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood    protected SuggestionsAdapter<ListAdapter> mSuggestionsAdapter;
677010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
680a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected ImageButton mSearchGoButton;
690a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected ImageButton mVoiceSearchButton;
707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
710a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected ButtonsKeyListener mButtonsKeyListener;
727010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
737010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private boolean mUpdateSuggestions;
747010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
757010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private QueryListener mQueryListener;
767010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private SearchClickListener mSearchClickListener;
77b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    protected View.OnClickListener mExitClickListener;
787010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
7911234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    public SearchActivityView(Context context) {
80c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        super(context);
81c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    }
82c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
8311234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    public SearchActivityView(Context context, AttributeSet attrs) {
84c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        super(context, attrs);
85c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    }
86c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
8711234b9966c6b0e5c17d00e3b973c0d49a8d1f57Bjorn Bringert    public SearchActivityView(Context context, AttributeSet attrs, int defStyle) {
88c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        super(context, attrs, defStyle);
89c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    }
90c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
917010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    @Override
927010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void onFinishInflate() {
937010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView = (QueryTextView) findViewById(R.id.search_src_text);
947010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
957010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
967010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsView.setOnScrollListener(new InputMethodCloser());
977010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
987010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsView.setOnFocusChangeListener(new SuggestListFocusListener());
997010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1007010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsAdapter = createSuggestionsAdapter();
1017010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        // TODO: why do we need focus listeners both on the SuggestionsView and the individual
1027010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        // suggestions?
1037010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsAdapter.setOnFocusChangeListener(new SuggestListFocusListener());
1047010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1057010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
1067010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
1077a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood        mVoiceSearchButton.setImageDrawable(getVoiceSearchIcon());
1087010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1097010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.addTextChangedListener(new SearchTextWatcher());
110748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood        mQueryTextView.setOnEditorActionListener(new QueryTextEditorActionListener());
1117010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
1127010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextEmptyBg = mQueryTextView.getBackground();
1137010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1147010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchGoButton.setOnClickListener(new SearchGoButtonClickListener());
1157010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1160a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mButtonsKeyListener = new ButtonsKeyListener();
1170a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mSearchGoButton.setOnKeyListener(mButtonsKeyListener);
1180a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        mVoiceSearchButton.setOnKeyListener(mButtonsKeyListener);
1197010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mUpdateSuggestions = true;
1217010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1230a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    public abstract void onResume();
1240a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood
1250a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    public abstract void onStop();
1260a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood
1272e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    public void onPause() {
1282e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani        // Override if necessary
1292e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani    }
1302e684983c6a20faa209e42f6c63337cfcb34fc71Amith Yamasani
1317010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void start() {
132fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood        mSuggestionsAdapter.getListAdapter().registerDataSetObserver(new SuggestionsObserver());
133fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood        mSuggestionsView.setSuggestionsAdapter(mSuggestionsAdapter);
1347010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1357010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void destroy() {
137fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood        mSuggestionsView.setSuggestionsAdapter(null);  // closes mSuggestionsAdapter
1387010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1395880fdc4f6fef3c9b5b95a49a0f23c37c69f89d5Bjorn Bringert
1407010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // TODO: Get rid of this. To make it more easily testable,
1417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    // the SearchActivityView should not depend on QsbApplication.
1427010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected QsbApplication getQsbApplication() {
1437010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return QsbApplication.get(getContext());
1447010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1457010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1467a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood    protected Drawable getVoiceSearchIcon() {
1477a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood        return getResources().getDrawable(R.drawable.ic_btn_speak_now);
1487a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood    }
1497a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood
1507a427f9830f7f9201299fae34a93016c77b0a569Mathew Inwood    protected VoiceSearch getVoiceSearch() {
1517010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return getQsbApplication().getVoiceSearch();
1527010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
154fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood    protected SuggestionsAdapter<ListAdapter> createSuggestionsAdapter() {
155fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood        return new DelayingSuggestionsAdapter<ListAdapter>(new SuggestionsListAdapter(
15677909685887bd6db7454b73cf274afc3aca2f58dBjorn Bringert                getQsbApplication().getSuggestionViewFactory()));
1577010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1587010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
159f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood    public void setMaxPromotedResults(int maxPromoted) {
160f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood    }
161f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood
162f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood    public void limitResultsToViewHeight() {
163f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood    }
164f5a8912d5da80378d38b667eba4aaa0555aea7bdMathew Inwood
1657010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setQueryListener(QueryListener listener) {
1667010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryListener = listener;
1677010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1687010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1697010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setSearchClickListener(SearchClickListener listener) {
1707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSearchClickListener = listener;
1717010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1727010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1737010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setVoiceSearchButtonClickListener(View.OnClickListener listener) {
1747010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (mVoiceSearchButton != null) {
1757010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mVoiceSearchButton.setOnClickListener(listener);
1767010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
1777010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1787010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1797010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setSuggestionClickListener(final SuggestionClickListener listener) {
1807010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsAdapter.setSuggestionClickListener(listener);
1817010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.setCommitCompletionListener(new QueryTextView.CommitCompletionListener() {
1827010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            @Override
1837010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            public void onCommitCompletion(int position) {
1847010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                mSuggestionsAdapter.onSuggestionClicked(position);
1857010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
1867010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        });
1877010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1887010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
1899e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood    public void setExitClickListener(final View.OnClickListener listener) {
1909e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood        mExitClickListener = listener;
1919e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood    }
1929e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood
1937010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public Suggestions getSuggestions() {
1947010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return mSuggestionsAdapter.getSuggestions();
1957010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
1967010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
197ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood    public SuggestionCursor getCurrentSuggestions() {
198ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        return mSuggestionsAdapter.getSuggestions().getResult();
1997010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2007010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2017010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setSuggestions(Suggestions suggestions) {
2027010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        suggestions.acquire();
2037010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsAdapter.setSuggestions(suggestions);
2047010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2057010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2067010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void clearSuggestions() {
2077010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mSuggestionsAdapter.setSuggestions(null);
2087010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2097010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2107010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public String getQuery() {
2117010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        CharSequence q = mQueryTextView.getText();
2127010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return q == null ? "" : q.toString();
2137010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2147010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
215b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    public boolean isQueryEmpty() {
216b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        return TextUtils.isEmpty(getQuery());
217b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    }
218b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert
2197010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
2207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Sets the text in the query box. Does not update the suggestions.
2217010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
2227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void setQuery(String query, boolean selectAll) {
2237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mUpdateSuggestions = false;
2247010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.setText(query);
2257010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.setTextSelection(selectAll);
2267010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mUpdateSuggestions = true;
2277010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2287010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2290a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood    protected SearchActivity getActivity() {
230c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        Context context = getContext();
2310a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood        if (context instanceof SearchActivity) {
2320a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood            return (SearchActivity) context;
233c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        } else {
234c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert            return null;
235c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        }
236c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    }
237c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
2388749e424d7645e708f238530621248abb567e3beBjorn Bringert    public void hideSuggestions() {
2398749e424d7645e708f238530621248abb567e3beBjorn Bringert        mSuggestionsView.setVisibility(GONE);
2408749e424d7645e708f238530621248abb567e3beBjorn Bringert    }
2418749e424d7645e708f238530621248abb567e3beBjorn Bringert
2428749e424d7645e708f238530621248abb567e3beBjorn Bringert    public void showSuggestions() {
2438749e424d7645e708f238530621248abb567e3beBjorn Bringert        mSuggestionsView.setVisibility(VISIBLE);
2448749e424d7645e708f238530621248abb567e3beBjorn Bringert    }
2458749e424d7645e708f238530621248abb567e3beBjorn Bringert
2467010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void focusQueryTextView() {
2477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.requestFocus();
2487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2508b2936607176720172aee068abc5631bdf77e843Bjorn Bringert    protected void updateUi() {
251b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        updateUi(isQueryEmpty());
2528b2936607176720172aee068abc5631bdf77e843Bjorn Bringert    }
2538b2936607176720172aee068abc5631bdf77e843Bjorn Bringert
2547010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void updateUi(boolean queryEmpty) {
2557010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        updateQueryTextView(queryEmpty);
2567010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        updateSearchGoButton(queryEmpty);
2577010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        updateVoiceSearchButton(queryEmpty);
2587010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2597010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
26055938341e72f1581d6caae51a31d8d72a1cd8138Amith Yamasani    protected void updateQueryTextView(boolean queryEmpty) {
2617010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (queryEmpty) {
262ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            mQueryTextView.setBackgroundDrawable(mQueryTextEmptyBg);
263ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            mQueryTextView.setHint(null);
2647010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        } else {
2657010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mQueryTextView.setBackgroundResource(R.drawable.textfield_search);
2667010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
2677010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2687010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2697010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private void updateSearchGoButton(boolean queryEmpty) {
2707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (queryEmpty) {
2717010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mSearchGoButton.setVisibility(View.GONE);
2727010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        } else {
2737010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mSearchGoButton.setVisibility(View.VISIBLE);
2747010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
2757010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2767010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2777010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void updateVoiceSearchButton(boolean queryEmpty) {
2783be3f12c41baa6c91ebb4fef366bd3d9bfcbc093Bjorn Bringert        if (shouldShowVoiceSearch(queryEmpty)
279ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood                && getVoiceSearch().shouldShowVoiceSearch()) {
2807010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mVoiceSearchButton.setVisibility(View.VISIBLE);
2817010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
2827010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        } else {
2837010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mVoiceSearchButton.setVisibility(View.GONE);
2847010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            mQueryTextView.setPrivateImeOptions(null);
2857010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
2867010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
2877010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
2883be3f12c41baa6c91ebb4fef366bd3d9bfcbc093Bjorn Bringert    protected boolean shouldShowVoiceSearch(boolean queryEmpty) {
2893be3f12c41baa6c91ebb4fef366bd3d9bfcbc093Bjorn Bringert        return queryEmpty;
2903be3f12c41baa6c91ebb4fef366bd3d9bfcbc093Bjorn Bringert    }
2913be3f12c41baa6c91ebb4fef366bd3d9bfcbc093Bjorn Bringert
292c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    /**
293c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert     * Hides the input method.
294c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert     */
295c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    protected void hideInputMethod() {
296c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        InputMethodManager imm = (InputMethodManager)
297c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
298c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        if (imm != null) {
299c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert            imm.hideSoftInputFromWindow(getWindowToken(), 0);
300c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert        }
301c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    }
302c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert
3038749e424d7645e708f238530621248abb567e3beBjorn Bringert    public abstract void considerHidingInputMethod();
3047010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3057010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public void showInputMethodForQuery() {
3067010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        mQueryTextView.showInputMethod();
3077010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
3087010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
309c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert    /**
310b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert     * Dismiss the activity if BACK is pressed when the search box is empty.
311b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert     */
312b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    @Override
313b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    public boolean dispatchKeyEventPreIme(KeyEvent event) {
314b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        SearchActivity activity = getActivity();
315b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        if (activity != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK
316b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                && isQueryEmpty()) {
317b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert            KeyEvent.DispatcherState state = getKeyDispatcherState();
318b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert            if (state != null) {
319b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                if (event.getAction() == KeyEvent.ACTION_DOWN
320b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                        && event.getRepeatCount() == 0) {
321b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                    state.startTracking(event, this);
322b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                    return true;
323b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                } else if (event.getAction() == KeyEvent.ACTION_UP
324b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                        && !event.isCanceled() && state.isTracking(event)) {
325b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                    hideInputMethod();
326b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                    activity.onBackPressed();
327b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                    return true;
328b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert                }
329b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert            }
330b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        }
331b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert        return super.dispatchKeyEventPreIme(event);
332b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    }
333b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert
334b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert    /**
3357010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * If the input method is in fullscreen mode, and the selector corpus
3367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * is All or Web, use the web search suggestions as completions.
3377010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
3387010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected void updateInputMethodSuggestions() {
3397010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        InputMethodManager imm = (InputMethodManager)
3407010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (imm == null || !imm.isFullscreenMode()) return;
3427010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        Suggestions suggestions = mSuggestionsAdapter.getSuggestions();
3437010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (suggestions == null) return;
3447010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        CompletionInfo[] completions = webSuggestionsToCompletions(suggestions);
3457010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (DBG) Log.d(TAG, "displayCompletions(" + Arrays.toString(completions) + ")");
3467010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        imm.displayCompletions(mQueryTextView, completions);
3477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
3487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private CompletionInfo[] webSuggestionsToCompletions(Suggestions suggestions) {
350ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        SourceResult cursor = suggestions.getWebResult();
3517010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (cursor == null) return null;
3527010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        int count = cursor.getCount();
3537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        ArrayList<CompletionInfo> completions = new ArrayList<CompletionInfo>(count);
3547010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        for (int i = 0; i < count; i++) {
3557010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            cursor.moveTo(i);
356ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            String text1 = cursor.getSuggestionText1();
357ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood            completions.add(new CompletionInfo(i, i, text1));
3587010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
3597010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return completions.toArray(new CompletionInfo[completions.size()]);
3607010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
3617010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3628b2936607176720172aee068abc5631bdf77e843Bjorn Bringert    protected void onSuggestionsChanged() {
3638b2936607176720172aee068abc5631bdf77e843Bjorn Bringert        updateInputMethodSuggestions();
3648b2936607176720172aee068abc5631bdf77e843Bjorn Bringert    }
3658b2936607176720172aee068abc5631bdf77e843Bjorn Bringert
366fd4a4cbc1143a734d357897531daa7105db6459bMathew Inwood    protected boolean onSuggestionKeyDown(SuggestionsAdapter<?> adapter,
36745f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood            long suggestionId, int keyCode, KeyEvent event) {
3687010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        // Treat enter or search as a click
3697010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (       keyCode == KeyEvent.KEYCODE_ENTER
3707010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                || keyCode == KeyEvent.KEYCODE_SEARCH
3717010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
3720a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwood            if (adapter != null) {
37345f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                adapter.onSuggestionClicked(suggestionId);
3747010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return true;
3757010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            } else {
3767010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return false;
3777010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
3787010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
3797010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3807010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return false;
3817010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
3827010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3837010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected boolean onSearchClicked(int method) {
3847010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        if (mSearchClickListener != null) {
3857010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            return mSearchClickListener.onSearchClicked(method);
3867010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
3877010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return false;
3887010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
3897010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
3907010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
3917010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Filters the suggestions list when the search text changes.
3927010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
3937010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class SearchTextWatcher implements TextWatcher {
394ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
3957010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void afterTextChanged(Editable s) {
3967010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            boolean empty = s.length() == 0;
3977010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (empty != mQueryWasEmpty) {
3987010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                mQueryWasEmpty = empty;
3997010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                updateUi(empty);
4007010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
4017010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (mUpdateSuggestions) {
4027010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                if (mQueryListener != null) {
4037010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                    mQueryListener.onQueryChanged();
4047010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                }
4057010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
4067010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4077010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
408ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4097010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
4107010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4117010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
412ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4137010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onTextChanged(CharSequence s, int start, int before, int count) {
4147010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4157010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4167010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4177010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
4187010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Handles key events on the suggestions list view.
4197010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
4207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    protected class SuggestionsViewKeyListener implements View.OnKeyListener {
421ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
4237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (event.getAction() == KeyEvent.ACTION_DOWN
42445f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                    && v instanceof SuggestionsListView<?>) {
42545f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                SuggestionsListView<?> listView = (SuggestionsListView<?>) v;
42645f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                if (onSuggestionKeyDown(listView.getSuggestionsAdapter(),
42745f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                        listView.getSelectedItemId(), keyCode, event)) {
4287010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                    return true;
4297010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                }
4307010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
4317010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
4327010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4337010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4347010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4357010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class InputMethodCloser implements SuggestionsView.OnScrollListener {
4367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
437ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4387010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
4397010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                int totalItemCount) {
4407010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4417010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
442ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4437010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onScrollStateChanged(AbsListView view, int scrollState) {
4447010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            considerHidingInputMethod();
4457010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4467010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
4497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Listens for clicks on the source selector.
4507010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
4517010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class SearchGoButtonClickListener implements View.OnClickListener {
452ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4537010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onClick(View view) {
4547010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            onSearchClicked(Logger.SEARCH_METHOD_BUTTON);
4557010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4567010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4577010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4587010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
459748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood     * This class handles enter key presses in the query text view.
4607010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
461748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood    private class QueryTextEditorActionListener implements OnEditorActionListener {
462ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
463748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
464748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood            boolean consumed = false;
465748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood            if (event != null) {
466748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                if (event.getAction() == KeyEvent.ACTION_UP) {
467748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                    consumed = onSearchClicked(Logger.SEARCH_METHOD_KEYBOARD);
468748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
469748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                    // we have to consume the down event so that we receive the up event too
470748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                    consumed = true;
471748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood                }
4727010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
473748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood            if (DBG) Log.d(TAG, "onEditorAction consumed=" + consumed);
474748a3cd7cb44de0aedb05e3c5d19989e2e52164fMathew Inwood            return consumed;
4757010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4767010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4777010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4787010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
4797010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Handles key events on the search and voice search buttons,
4807010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * by refocusing to EditText.
4817010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
4827010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class ButtonsKeyListener implements View.OnKeyListener {
483ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
4847010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public boolean onKey(View v, int keyCode, KeyEvent event) {
4857010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            return forwardKeyToQueryTextView(keyCode, event);
4867010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4877010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4887010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
4897010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
49045f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood        if (!event.isSystem() && shouldForwardToQueryTextView(keyCode)) {
4917010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
4927010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (mQueryTextView.requestFocus()) {
4937010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return mQueryTextView.dispatchKeyEvent(event);
4947010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
4957010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
4967010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        return false;
4977010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
4987010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
49945f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood    private boolean shouldForwardToQueryTextView(int keyCode) {
5007010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        switch (keyCode) {
5017010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            case KeyEvent.KEYCODE_DPAD_UP:
5027010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            case KeyEvent.KEYCODE_DPAD_DOWN:
5037010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            case KeyEvent.KEYCODE_DPAD_LEFT:
5047010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            case KeyEvent.KEYCODE_DPAD_RIGHT:
5057010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            case KeyEvent.KEYCODE_DPAD_CENTER:
50645f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood            case KeyEvent.KEYCODE_ENTER:
50745f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood            case KeyEvent.KEYCODE_SEARCH:
5087010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                return false;
50945f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood            default:
51045f42cab0664a8a23975e8913dbe77c1fd8ad86cMathew Inwood                return true;
5117010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
5127010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5137010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5147010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    /**
5157010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     * Hides the input method when the suggestions get focus.
5167010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert     */
5177010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class SuggestListFocusListener implements OnFocusChangeListener {
518ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
5197010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
5207010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (DBG) Log.d(TAG, "Suggestions focus change, now: " + focused);
5217010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (focused) {
5227010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                considerHidingInputMethod();
5237010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
5247010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
5257010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5267010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5277010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    private class QueryTextViewFocusListener implements OnFocusChangeListener {
528ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
5297010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onFocusChange(View v, boolean focused) {
5307010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (DBG) Log.d(TAG, "Query focus change, now: " + focused);
5317010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            if (focused) {
5327010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                // The query box got focus, show the input method
5337010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert                showInputMethodForQuery();
5347010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert            }
5357010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
5367010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5377010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5387a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24Mathew Inwood    protected class SuggestionsObserver extends DataSetObserver {
5397010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        @Override
5407010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        public void onChanged() {
5418b2936607176720172aee068abc5631bdf77e843Bjorn Bringert            onSuggestionsChanged();
5427010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        }
5437010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5447010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5457010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public interface QueryListener {
5467010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        void onQueryChanged();
5477010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5487010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5497010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    public interface SearchClickListener {
5507010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert        boolean onSearchClicked(int method);
5517010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert    }
5527010c51b51c97fa43d7b24d2158ecbc1d064e0a6Bjorn Bringert
5532cbf1d099dbd04c612e3cba34a70293705730fa0Amith Yamasani    private class CloseClickListener implements OnClickListener {
554ecf356c15143ab0583c64682de16d94a57f7dd1cMathew Inwood        @Override
5552cbf1d099dbd04c612e3cba34a70293705730fa0Amith Yamasani        public void onClick(View v) {
556b28dcd61d3aec036c6a2b51571d11453ff6eb671Bjorn Bringert            if (!isQueryEmpty()) {
5579e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood                mQueryTextView.setText("");
5589e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            } else {
5599e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood                mExitClickListener.onClick(v);
5609e46c057d7f88d2a899e124f96d266c54250bb45Mathew Inwood            }
5612cbf1d099dbd04c612e3cba34a70293705730fa0Amith Yamasani        }
5622cbf1d099dbd04c612e3cba34a70293705730fa0Amith Yamasani    }
563c36735de23beadc017c484596b551d7c4eaff289Bjorn Bringert}
564