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