| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.quicksearchbox; |
| |
| import com.android.common.Search; |
| import com.android.quicksearchbox.ui.SearchActivityView; |
| import com.android.quicksearchbox.ui.SuggestionClickListener; |
| import com.android.quicksearchbox.ui.SuggestionsAdapter; |
| import com.android.quicksearchbox.util.Consumer; |
| import com.android.quicksearchbox.util.Consumers; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.CharMatcher; |
| |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.app.SearchManager; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.database.DataSetObserver; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Debug; |
| import android.os.Handler; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.View; |
| import android.widget.Toast; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * The main activity for Quick Search Box. Shows the search UI. |
| * |
| */ |
| public class SearchActivity extends Activity { |
| |
| private static final boolean DBG = false; |
| private static final String TAG = "QSB.SearchActivity"; |
| |
| private static final String SCHEME_CORPUS = "qsb.corpus"; |
| |
| public static final String INTENT_ACTION_QSB_AND_SELECT_CORPUS |
| = "com.android.quicksearchbox.action.QSB_AND_SELECT_CORPUS"; |
| |
| private static final String INTENT_EXTRA_TRACE_START_UP = "trace_start_up"; |
| |
| // Keys for the saved instance state. |
| private static final String INSTANCE_KEY_CORPUS = "corpus"; |
| private static final String INSTANCE_KEY_QUERY = "query"; |
| |
| private static final String ACTIVITY_HELP_CONTEXT = "search"; |
| |
| private boolean mTraceStartUp; |
| // Measures time from for last onCreate()/onNewIntent() call. |
| private LatencyTracker mStartLatencyTracker; |
| // Measures time spent inside onCreate() |
| private LatencyTracker mOnCreateTracker; |
| private int mOnCreateLatency; |
| // Whether QSB is starting. True between the calls to onCreate()/onNewIntent() and onResume(). |
| private boolean mStarting; |
| // True if the user has taken some action, e.g. launching a search, voice search, |
| // or suggestions, since QSB was last started. |
| private boolean mTookAction; |
| |
| private SearchActivityView mSearchActivityView; |
| |
| private CorporaObserver mCorporaObserver; |
| |
| private Bundle mAppSearchData; |
| |
| private final Handler mHandler = new Handler(); |
| private final Runnable mUpdateSuggestionsTask = new Runnable() { |
| public void run() { |
| updateSuggestions(); |
| } |
| }; |
| |
| private final Runnable mShowInputMethodTask = new Runnable() { |
| public void run() { |
| mSearchActivityView.showInputMethodForQuery(); |
| } |
| }; |
| |
| private OnDestroyListener mDestroyListener; |
| |
| /** Called when the activity is first created. */ |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| mTraceStartUp = getIntent().hasExtra(INTENT_EXTRA_TRACE_START_UP); |
| if (mTraceStartUp) { |
| String traceFile = new File(getDir("traces", 0), "qsb-start.trace").getAbsolutePath(); |
| Log.i(TAG, "Writing start-up trace to " + traceFile); |
| Debug.startMethodTracing(traceFile); |
| } |
| recordStartTime(); |
| if (DBG) Log.d(TAG, "onCreate()"); |
| super.onCreate(savedInstanceState); |
| |
| // This forces the HTTP request to check the users domain to be |
| // sent as early as possible. |
| QsbApplication.get(this).getSearchBaseUrlHelper(); |
| |
| mSearchActivityView = setupContentView(); |
| |
| if (getConfig().showScrollingSuggestions()) { |
| mSearchActivityView.setMaxPromotedSuggestions(getConfig().getMaxPromotedSuggestions()); |
| } else { |
| mSearchActivityView.limitSuggestionsToViewHeight(); |
| } |
| if (getConfig().showScrollingResults()) { |
| mSearchActivityView.setMaxPromotedResults(getConfig().getMaxPromotedResults()); |
| } else { |
| mSearchActivityView.limitResultsToViewHeight(); |
| } |
| |
| mSearchActivityView.setSearchClickListener(new SearchActivityView.SearchClickListener() { |
| public boolean onSearchClicked(int method) { |
| return SearchActivity.this.onSearchClicked(method); |
| } |
| }); |
| |
| mSearchActivityView.setQueryListener(new SearchActivityView.QueryListener() { |
| public void onQueryChanged() { |
| updateSuggestionsBuffered(); |
| } |
| }); |
| |
| mSearchActivityView.setSuggestionClickListener(new ClickHandler()); |
| |
| mSearchActivityView.setVoiceSearchButtonClickListener(new View.OnClickListener() { |
| public void onClick(View view) { |
| onVoiceSearchClicked(); |
| } |
| }); |
| |
| View.OnClickListener finishOnClick = new View.OnClickListener() { |
| public void onClick(View v) { |
| finish(); |
| } |
| }; |
| mSearchActivityView.setExitClickListener(finishOnClick); |
| |
| // First get setup from intent |
| Intent intent = getIntent(); |
| setupFromIntent(intent); |
| // Then restore any saved instance state |
| restoreInstanceState(savedInstanceState); |
| |
| // Do this at the end, to avoid updating the list view when setSource() |
| // is called. |
| mSearchActivityView.start(); |
| |
| mCorporaObserver = new CorporaObserver(); |
| getCorpora().registerDataSetObserver(mCorporaObserver); |
| recordOnCreateDone(); |
| } |
| |
| protected SearchActivityView setupContentView() { |
| setContentView(R.layout.search_activity); |
| return (SearchActivityView) findViewById(R.id.search_activity_view); |
| } |
| |
| protected SearchActivityView getSearchActivityView() { |
| return mSearchActivityView; |
| } |
| |
| @Override |
| protected void onNewIntent(Intent intent) { |
| if (DBG) Log.d(TAG, "onNewIntent()"); |
| recordStartTime(); |
| setIntent(intent); |
| setupFromIntent(intent); |
| } |
| |
| private void recordStartTime() { |
| mStartLatencyTracker = new LatencyTracker(); |
| mOnCreateTracker = new LatencyTracker(); |
| mStarting = true; |
| mTookAction = false; |
| } |
| |
| private void recordOnCreateDone() { |
| mOnCreateLatency = mOnCreateTracker.getLatency(); |
| } |
| |
| protected void restoreInstanceState(Bundle savedInstanceState) { |
| if (savedInstanceState == null) return; |
| String corpusName = savedInstanceState.getString(INSTANCE_KEY_CORPUS); |
| String query = savedInstanceState.getString(INSTANCE_KEY_QUERY); |
| setCorpus(corpusName); |
| setQuery(query, false); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| // We don't save appSearchData, since we always get the value |
| // from the intent and the user can't change it. |
| |
| outState.putString(INSTANCE_KEY_CORPUS, getCorpusName()); |
| outState.putString(INSTANCE_KEY_QUERY, getQuery()); |
| } |
| |
| private void setupFromIntent(Intent intent) { |
| if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")"); |
| String corpusName = getCorpusNameFromUri(intent.getData()); |
| String query = intent.getStringExtra(SearchManager.QUERY); |
| Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA); |
| boolean selectAll = intent.getBooleanExtra(SearchManager.EXTRA_SELECT_QUERY, false); |
| |
| setCorpus(corpusName); |
| setQuery(query, selectAll); |
| mAppSearchData = appSearchData; |
| |
| if (startedIntoCorpusSelectionDialog()) { |
| mSearchActivityView.showCorpusSelectionDialog(); |
| } |
| } |
| |
| public boolean startedIntoCorpusSelectionDialog() { |
| return INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(getIntent().getAction()); |
| } |
| |
| /** |
| * Removes corpus selector intent action, so that BACK works normally after |
| * dismissing and reopening the corpus selector. |
| */ |
| public void clearStartedIntoCorpusSelectionDialog() { |
| Intent oldIntent = getIntent(); |
| if (SearchActivity.INTENT_ACTION_QSB_AND_SELECT_CORPUS.equals(oldIntent.getAction())) { |
| Intent newIntent = new Intent(oldIntent); |
| newIntent.setAction(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); |
| setIntent(newIntent); |
| } |
| } |
| |
| public static Uri getCorpusUri(Corpus corpus) { |
| if (corpus == null) return null; |
| return new Uri.Builder() |
| .scheme(SCHEME_CORPUS) |
| .authority(corpus.getName()) |
| .build(); |
| } |
| |
| private String getCorpusNameFromUri(Uri uri) { |
| if (uri == null) return null; |
| if (!SCHEME_CORPUS.equals(uri.getScheme())) return null; |
| return uri.getAuthority(); |
| } |
| |
| private Corpus getCorpus() { |
| return mSearchActivityView.getCorpus(); |
| } |
| |
| private String getCorpusName() { |
| return mSearchActivityView.getCorpusName(); |
| } |
| |
| private void setCorpus(String name) { |
| mSearchActivityView.setCorpus(name); |
| } |
| |
| private QsbApplication getQsbApplication() { |
| return QsbApplication.get(this); |
| } |
| |
| private Config getConfig() { |
| return getQsbApplication().getConfig(); |
| } |
| |
| protected SearchSettings getSettings() { |
| return getQsbApplication().getSettings(); |
| } |
| |
| private Corpora getCorpora() { |
| return getQsbApplication().getCorpora(); |
| } |
| |
| private CorpusRanker getCorpusRanker() { |
| return getQsbApplication().getCorpusRanker(); |
| } |
| |
| private ShortcutRepository getShortcutRepository() { |
| return getQsbApplication().getShortcutRepository(); |
| } |
| |
| private SuggestionsProvider getSuggestionsProvider() { |
| return getQsbApplication().getSuggestionsProvider(); |
| } |
| |
| private Logger getLogger() { |
| return getQsbApplication().getLogger(); |
| } |
| |
| @VisibleForTesting |
| public void setOnDestroyListener(OnDestroyListener l) { |
| mDestroyListener = l; |
| } |
| |
| @Override |
| protected void onDestroy() { |
| if (DBG) Log.d(TAG, "onDestroy()"); |
| getCorpora().unregisterDataSetObserver(mCorporaObserver); |
| mSearchActivityView.destroy(); |
| super.onDestroy(); |
| if (mDestroyListener != null) { |
| mDestroyListener.onDestroyed(); |
| } |
| } |
| |
| @Override |
| protected void onStop() { |
| if (DBG) Log.d(TAG, "onStop()"); |
| if (!mTookAction) { |
| // TODO: This gets logged when starting other activities, e.g. by opening the search |
| // settings, or clicking a notification in the status bar. |
| // TODO we should log both sets of suggestions in 2-pane mode |
| getLogger().logExit(getCurrentSuggestions(), getQuery().length()); |
| } |
| // Close all open suggestion cursors. The query will be redone in onResume() |
| // if we come back to this activity. |
| mSearchActivityView.clearSuggestions(); |
| getQsbApplication().getShortcutRefresher().reset(); |
| mSearchActivityView.onStop(); |
| super.onStop(); |
| } |
| |
| @Override |
| protected void onPause() { |
| if (DBG) Log.d(TAG, "onPause()"); |
| mSearchActivityView.onPause(); |
| super.onPause(); |
| } |
| |
| @Override |
| protected void onRestart() { |
| if (DBG) Log.d(TAG, "onRestart()"); |
| super.onRestart(); |
| } |
| |
| @Override |
| protected void onResume() { |
| if (DBG) Log.d(TAG, "onResume()"); |
| super.onResume(); |
| updateSuggestionsBuffered(); |
| mSearchActivityView.onResume(); |
| if (mTraceStartUp) Debug.stopMethodTracing(); |
| } |
| |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| // Since the menu items are dynamic, we recreate the menu every time. |
| menu.clear(); |
| createMenuItems(menu, true); |
| return true; |
| } |
| |
| public void createMenuItems(Menu menu, boolean showDisabled) { |
| getSettings().addMenuItems(menu, showDisabled); |
| getQsbApplication().getHelp().addHelpMenuItem(menu, ACTIVITY_HELP_CONTEXT); |
| } |
| |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| super.onWindowFocusChanged(hasFocus); |
| if (hasFocus) { |
| // Launch the IME after a bit |
| mHandler.postDelayed(mShowInputMethodTask, 0); |
| } |
| } |
| |
| protected String getQuery() { |
| return mSearchActivityView.getQuery(); |
| } |
| |
| protected void setQuery(String query, boolean selectAll) { |
| mSearchActivityView.setQuery(query, selectAll); |
| } |
| |
| public CorpusSelectionDialog getCorpusSelectionDialog() { |
| CorpusSelectionDialog dialog = createCorpusSelectionDialog(); |
| dialog.setOwnerActivity(this); |
| dialog.setOnDismissListener(new CorpusSelectorDismissListener()); |
| return dialog; |
| } |
| |
| protected CorpusSelectionDialog createCorpusSelectionDialog() { |
| return new CorpusSelectionDialog(this, getSettings()); |
| } |
| |
| /** |
| * @return true if a search was performed as a result of this click, false otherwise. |
| */ |
| protected boolean onSearchClicked(int method) { |
| String query = CharMatcher.WHITESPACE.trimAndCollapseFrom(getQuery(), ' '); |
| if (DBG) Log.d(TAG, "Search clicked, query=" + query); |
| |
| // Don't do empty queries |
| if (TextUtils.getTrimmedLength(query) == 0) return false; |
| |
| Corpus searchCorpus = getSearchCorpus(); |
| if (searchCorpus == null) return false; |
| |
| mTookAction = true; |
| |
| // Log search start |
| getLogger().logSearch(getCorpus(), method, query.length()); |
| |
| // Start search |
| startSearch(searchCorpus, query); |
| return true; |
| } |
| |
| protected void startSearch(Corpus searchCorpus, String query) { |
| Intent intent = searchCorpus.createSearchIntent(query, mAppSearchData); |
| launchIntent(intent); |
| } |
| |
| protected void onVoiceSearchClicked() { |
| if (DBG) Log.d(TAG, "Voice Search clicked"); |
| Corpus searchCorpus = getSearchCorpus(); |
| if (searchCorpus == null) return; |
| |
| mTookAction = true; |
| |
| // Log voice search start |
| getLogger().logVoiceSearch(searchCorpus); |
| |
| // Start voice search |
| Intent intent = searchCorpus.createVoiceSearchIntent(mAppSearchData); |
| launchIntent(intent); |
| } |
| |
| protected Corpus getSearchCorpus() { |
| return mSearchActivityView.getSearchCorpus(); |
| } |
| |
| protected SuggestionCursor getCurrentSuggestions() { |
| return mSearchActivityView.getCurrentPromotedSuggestions(); |
| } |
| |
| protected SuggestionPosition getCurrentSuggestions(SuggestionsAdapter<?> adapter, long id) { |
| SuggestionPosition pos = adapter.getSuggestion(id); |
| if (pos == null) { |
| return null; |
| } |
| SuggestionCursor suggestions = pos.getCursor(); |
| int position = pos.getPosition(); |
| if (suggestions == null) { |
| return null; |
| } |
| int count = suggestions.getCount(); |
| if (position < 0 || position >= count) { |
| Log.w(TAG, "Invalid suggestion position " + position + ", count = " + count); |
| return null; |
| } |
| suggestions.moveTo(position); |
| return pos; |
| } |
| |
| protected Set<Corpus> getCurrentIncludedCorpora() { |
| Suggestions suggestions = mSearchActivityView.getSuggestions(); |
| return suggestions == null ? null : suggestions.getIncludedCorpora(); |
| } |
| |
| protected void launchIntent(Intent intent) { |
| if (DBG) Log.d(TAG, "launchIntent " + intent); |
| if (intent == null) { |
| return; |
| } |
| try { |
| startActivity(intent); |
| } catch (RuntimeException ex) { |
| // Since the intents for suggestions specified by suggestion providers, |
| // guard against them not being handled, not allowed, etc. |
| Log.e(TAG, "Failed to start " + intent.toUri(0), ex); |
| } |
| } |
| |
| private boolean launchSuggestion(SuggestionsAdapter<?> adapter, long id) { |
| SuggestionPosition suggestion = getCurrentSuggestions(adapter, id); |
| if (suggestion == null) return false; |
| |
| if (DBG) Log.d(TAG, "Launching suggestion " + id); |
| mTookAction = true; |
| |
| // Log suggestion click |
| getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(), |
| Logger.SUGGESTION_CLICK_TYPE_LAUNCH); |
| |
| // Create shortcut |
| getShortcutRepository().reportClick(suggestion.getCursor(), suggestion.getPosition()); |
| |
| // Launch intent |
| launchSuggestion(suggestion.getCursor(), suggestion.getPosition()); |
| |
| return true; |
| } |
| |
| protected void launchSuggestion(SuggestionCursor suggestions, int position) { |
| suggestions.moveTo(position); |
| Intent intent = SuggestionUtils.getSuggestionIntent(suggestions, mAppSearchData); |
| launchIntent(intent); |
| } |
| |
| protected void removeFromHistoryClicked(final SuggestionsAdapter<?> adapter, |
| final long id) { |
| SuggestionPosition suggestion = getCurrentSuggestions(adapter, id); |
| if (suggestion == null) return; |
| CharSequence title = suggestion.getSuggestionText1(); |
| AlertDialog dialog = new AlertDialog.Builder(this) |
| .setTitle(title) |
| .setMessage(R.string.remove_from_history) |
| .setPositiveButton(android.R.string.ok, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| // TODO: what if the suggestions have changed? |
| removeFromHistory(adapter, id); |
| } |
| }) |
| .setNegativeButton(android.R.string.cancel, null) |
| .create(); |
| dialog.show(); |
| } |
| |
| protected void removeFromHistory(SuggestionsAdapter<?> adapter, long id) { |
| SuggestionPosition suggestion = getCurrentSuggestions(adapter, id); |
| if (suggestion == null) return; |
| removeFromHistory(suggestion.getCursor(), suggestion.getPosition()); |
| // TODO: Log to event log? |
| } |
| |
| protected void removeFromHistory(SuggestionCursor suggestions, int position) { |
| removeShortcut(suggestions, position); |
| removeFromHistoryDone(true); |
| } |
| |
| protected void removeFromHistoryDone(boolean ok) { |
| Log.i(TAG, "Removed query from history, success=" + ok); |
| updateSuggestionsBuffered(); |
| if (!ok) { |
| Toast.makeText(this, R.string.remove_from_history_failed, Toast.LENGTH_SHORT).show(); |
| } |
| } |
| |
| protected void removeShortcut(SuggestionCursor suggestions, int position) { |
| if (suggestions.isSuggestionShortcut()) { |
| if (DBG) Log.d(TAG, "Removing suggestion " + position + " from shortcuts"); |
| getShortcutRepository().removeFromHistory(suggestions, position); |
| } |
| } |
| |
| protected void clickedQuickContact(SuggestionsAdapter<?> adapter, long id) { |
| SuggestionPosition suggestion = getCurrentSuggestions(adapter, id); |
| if (suggestion == null) return; |
| |
| if (DBG) Log.d(TAG, "Used suggestion " + suggestion.getPosition()); |
| mTookAction = true; |
| |
| // Log suggestion click |
| getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(), |
| Logger.SUGGESTION_CLICK_TYPE_QUICK_CONTACT); |
| |
| // Create shortcut |
| getShortcutRepository().reportClick(suggestion.getCursor(), suggestion.getPosition()); |
| } |
| |
| protected void refineSuggestion(SuggestionsAdapter<?> adapter, long id) { |
| if (DBG) Log.d(TAG, "query refine clicked, pos " + id); |
| SuggestionPosition suggestion = getCurrentSuggestions(adapter, id); |
| if (suggestion == null) { |
| return; |
| } |
| String query = suggestion.getSuggestionQuery(); |
| if (TextUtils.isEmpty(query)) { |
| return; |
| } |
| |
| // Log refine click |
| getLogger().logSuggestionClick(id, suggestion.getCursor(), getCurrentIncludedCorpora(), |
| Logger.SUGGESTION_CLICK_TYPE_REFINE); |
| |
| // Put query + space in query text view |
| String queryWithSpace = query + ' '; |
| setQuery(queryWithSpace, false); |
| updateSuggestions(); |
| mSearchActivityView.focusQueryTextView(); |
| } |
| |
| private void updateSuggestionsBuffered() { |
| if (DBG) Log.d(TAG, "updateSuggestionsBuffered()"); |
| mHandler.removeCallbacks(mUpdateSuggestionsTask); |
| long delay = getConfig().getTypingUpdateSuggestionsDelayMillis(); |
| mHandler.postDelayed(mUpdateSuggestionsTask, delay); |
| } |
| |
| private void gotSuggestions(Suggestions suggestions) { |
| if (mStarting) { |
| mStarting = false; |
| String source = getIntent().getStringExtra(Search.SOURCE); |
| int latency = mStartLatencyTracker.getLatency(); |
| getLogger().logStart(mOnCreateLatency, latency, source, getCorpus(), |
| suggestions == null ? null : suggestions.getExpectedCorpora()); |
| getQsbApplication().onStartupComplete(); |
| } |
| } |
| |
| private void getCorporaToQuery(Consumer<List<Corpus>> consumer) { |
| Corpus corpus = getCorpus(); |
| if (corpus == null) { |
| getCorpusRanker().getCorporaInAll(Consumers.createAsyncConsumer(mHandler, consumer)); |
| } else { |
| List<Corpus> corpora = new ArrayList<Corpus>(); |
| Corpus searchCorpus = getSearchCorpus(); |
| if (searchCorpus != null) corpora.add(searchCorpus); |
| consumer.consume(corpora); |
| } |
| } |
| |
| protected void getShortcutsForQuery(String query, Collection<Corpus> corporaToQuery, |
| final Suggestions suggestions) { |
| ShortcutRepository shortcutRepo = getShortcutRepository(); |
| if (shortcutRepo == null) return; |
| if (query.length() == 0 && !getConfig().showShortcutsForZeroQuery()) { |
| return; |
| } |
| Consumer<ShortcutCursor> consumer = Consumers.createAsyncCloseableConsumer(mHandler, |
| new Consumer<ShortcutCursor>() { |
| public boolean consume(ShortcutCursor shortcuts) { |
| suggestions.setShortcuts(shortcuts); |
| return true; |
| } |
| }); |
| shortcutRepo.getShortcutsForQuery(query, corporaToQuery, |
| getSettings().allowWebSearchShortcuts(), consumer); |
| } |
| |
| public void updateSuggestions() { |
| if (DBG) Log.d(TAG, "updateSuggestions()"); |
| final String query = CharMatcher.WHITESPACE.trimLeadingFrom(getQuery()); |
| getQsbApplication().getSourceTaskExecutor().cancelPendingTasks(); |
| getCorporaToQuery(new Consumer<List<Corpus>>(){ |
| @Override |
| public boolean consume(List<Corpus> corporaToQuery) { |
| updateSuggestions(query, corporaToQuery); |
| return true; |
| } |
| }); |
| } |
| |
| protected void updateSuggestions(String query, List<Corpus> corporaToQuery) { |
| if (DBG) Log.d(TAG, "updateSuggestions(\"" + query+"\"," + corporaToQuery + ")"); |
| Suggestions suggestions = getSuggestionsProvider().getSuggestions( |
| query, corporaToQuery); |
| getShortcutsForQuery(query, corporaToQuery, suggestions); |
| |
| // Log start latency if this is the first suggestions update |
| gotSuggestions(suggestions); |
| |
| showSuggestions(suggestions); |
| } |
| |
| protected void showSuggestions(Suggestions suggestions) { |
| mSearchActivityView.setSuggestions(suggestions); |
| } |
| |
| private class ClickHandler implements SuggestionClickListener { |
| |
| public void onSuggestionQuickContactClicked(SuggestionsAdapter<?> adapter, long id) { |
| clickedQuickContact(adapter, id); |
| } |
| |
| public void onSuggestionClicked(SuggestionsAdapter<?> adapter, long id) { |
| launchSuggestion(adapter, id); |
| } |
| |
| public void onSuggestionRemoveFromHistoryClicked(SuggestionsAdapter<?> adapter, long id) { |
| removeFromHistoryClicked(adapter, id); |
| } |
| |
| public void onSuggestionQueryRefineClicked(SuggestionsAdapter<?> adapter, long id) { |
| refineSuggestion(adapter, id); |
| } |
| } |
| |
| private class CorpusSelectorDismissListener implements DialogInterface.OnDismissListener { |
| public void onDismiss(DialogInterface dialog) { |
| if (DBG) Log.d(TAG, "Corpus selector dismissed"); |
| clearStartedIntoCorpusSelectionDialog(); |
| } |
| } |
| |
| private class CorporaObserver extends DataSetObserver { |
| @Override |
| public void onChanged() { |
| setCorpus(getCorpusName()); |
| updateSuggestions(); |
| } |
| } |
| |
| public interface OnDestroyListener { |
| void onDestroyed(); |
| } |
| |
| } |