Show web suggestions as fullscreen IME completions.
Bug: 2671161
Change-Id: Ie2afcb90929970ef08f40e8ad91d37078f17bde2
diff --git a/res/layout/search_activity.xml b/res/layout/search_activity.xml
index 6161dbd..aefb38c 100644
--- a/res/layout/search_activity.xml
+++ b/res/layout/search_activity.xml
@@ -62,7 +62,7 @@
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip" >
- <EditText
+ <com.android.quicksearchbox.ui.QueryTextView
android:id="@+id/search_src_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/src/com/android/quicksearchbox/AbstractSuggestionCursor.java b/src/com/android/quicksearchbox/AbstractSuggestionCursor.java
index 718aeda..31fd923 100644
--- a/src/com/android/quicksearchbox/AbstractSuggestionCursor.java
+++ b/src/com/android/quicksearchbox/AbstractSuggestionCursor.java
@@ -77,4 +77,8 @@
return intent;
}
+ public boolean isWebSearchSuggestion() {
+ return Intent.ACTION_WEB_SEARCH.equals(getSuggestionIntentAction());
+ }
+
}
diff --git a/src/com/android/quicksearchbox/SearchActivity.java b/src/com/android/quicksearchbox/SearchActivity.java
index 6898ea1..2a051d3 100644
--- a/src/com/android/quicksearchbox/SearchActivity.java
+++ b/src/com/android/quicksearchbox/SearchActivity.java
@@ -18,6 +18,7 @@
import com.android.common.Search;
import com.android.quicksearchbox.ui.CorpusViewFactory;
+import com.android.quicksearchbox.ui.QueryTextView;
import com.android.quicksearchbox.ui.SuggestionClickListener;
import com.android.quicksearchbox.ui.SuggestionsAdapter;
import com.android.quicksearchbox.ui.SuggestionsView;
@@ -40,12 +41,14 @@
import android.view.Menu;
import android.view.View;
import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
-import android.widget.EditText;
import android.widget.ImageButton;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
/**
@@ -86,7 +89,7 @@
private CorporaObserver mCorporaObserver;
- protected EditText mQueryTextView;
+ protected QueryTextView mQueryTextView;
// True if the query was empty on the previous call to updateQuery()
protected boolean mQueryWasEmpty = true;
@@ -126,7 +129,7 @@
setContentView();
mSuggestionsAdapter = getQsbApplication().createSuggestionsAdapter();
- mQueryTextView = (EditText) findViewById(R.id.search_src_text);
+ mQueryTextView = (QueryTextView) findViewById(R.id.search_src_text);
mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
mSuggestionsView.setSuggestionClickListener(new ClickHandler());
mSuggestionsView.setOnScrollListener(new InputMethodCloser());
@@ -142,6 +145,7 @@
mQueryTextView.addTextChangedListener(new SearchTextWatcher());
mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
+ mQueryTextView.setSuggestionClickListener(new ClickHandler());
mCorpusIndicator.setOnClickListener(new CorpusIndicatorClickListener());
@@ -162,6 +166,8 @@
// Then restore any saved instance state
restoreInstanceState(savedInstanceState);
+ mSuggestionsAdapter.registerDataSetObserver(new SuggestionsObserver());
+
// Do this at the end, to avoid updating the list view when setSource()
// is called.
mSuggestionsView.setAdapter(mSuggestionsAdapter);
@@ -379,33 +385,15 @@
}
/**
- * Sets the text in the query box. Does not update the suggestions,
- * and does not change the saved user-entered query.
- * {@link #restoreUserQuery()} will restore the query to the last
- * user-entered query.
+ * Sets the text in the query box. Does not update the suggestions.
*/
private void setQuery(String query, boolean selectAll) {
mUpdateSuggestions = false;
mQueryTextView.setText(query);
- setTextSelection(selectAll);
+ mQueryTextView.setTextSelection(selectAll);
mUpdateSuggestions = true;
}
- /**
- * Sets the text selection in the query text view.
- *
- * @param selectAll If {@code true}, selects the entire query.
- * If {@false}, no characters are selected, and the cursor is placed
- * at the end of the query.
- */
- private void setTextSelection(boolean selectAll) {
- if (selectAll) {
- mQueryTextView.selectAll();
- } else {
- mQueryTextView.setSelection(mQueryTextView.length());
- }
- }
-
protected void updateUi(boolean queryEmpty) {
updateQueryTextView(queryEmpty);
updateSearchGoButton(queryEmpty);
@@ -527,6 +515,14 @@
}
}
+ /**
+ * Checks if the corpus used for typed searchs is the web corpus.
+ */
+ protected boolean isSearchCorpusWeb() {
+ Corpus corpus = getSearchCorpus();
+ return corpus != null && corpus.isWebCorpus();
+ }
+
protected SuggestionCursor getCurrentSuggestions() {
return mSuggestionsAdapter.getCurrentSuggestions();
}
@@ -591,17 +587,11 @@
* Hides the input method.
*/
protected void hideInputMethod() {
- InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(mQueryTextView.getWindowToken(), 0);
- }
+ mQueryTextView.hideInputMethod();
}
protected void showInputMethodForQuery() {
- InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(mQueryTextView, 0);
- }
+ mQueryTextView.showInputMethod();
}
protected void onSuggestionListFocusChange(boolean focused) {
@@ -665,6 +655,36 @@
mSuggestionsAdapter.setSuggestions(suggestions);
}
+ /**
+ * If the input method is in fullscreen mode, and the selector corpus
+ * is All or Web, use the web search suggestions as completions.
+ */
+ protected void updateInputMethodSuggestions() {
+ InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+ if (imm == null || !imm.isFullscreenMode()) return;
+ if (!isSearchCorpusWeb()) return;
+ Suggestions suggestions = mSuggestionsAdapter.getSuggestions();
+ if (suggestions == null) return;
+ SuggestionCursor cursor = suggestions.getPromoted();
+ if (cursor == null) return;
+ CompletionInfo[] completions = webSuggestionsToCompletions(cursor);
+ if (DBG) Log.d(TAG, "displayCompletions(" + Arrays.toString(completions) + ")");
+ imm.displayCompletions(mQueryTextView, completions);
+ }
+
+ private CompletionInfo[] webSuggestionsToCompletions(SuggestionCursor cursor) {
+ int count = cursor.getCount();
+ ArrayList<CompletionInfo> completions = new ArrayList<CompletionInfo>(count);
+ for (int i = 0; i < count; i++) {
+ cursor.moveTo(i);
+ if (cursor.isWebSearchSuggestion()) {
+ String text1 = cursor.getSuggestionText1();
+ completions.add(new CompletionInfo(i, i, text1));
+ }
+ }
+ return completions.toArray(new CompletionInfo[completions.size()]);
+ }
+
private boolean forwardKeyToQueryTextView(int keyCode, KeyEvent event) {
if (!event.isSystem() && !isDpadKey(keyCode)) {
if (DBG) Log.d(TAG, "Forwarding key to query box: " + event);
@@ -837,6 +857,13 @@
}
}
+ private class SuggestionsObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ updateInputMethodSuggestions();
+ }
+ }
+
private static String ltrim(String text) {
int start = 0;
int length = text.length();
diff --git a/src/com/android/quicksearchbox/SuggestionCursor.java b/src/com/android/quicksearchbox/SuggestionCursor.java
index df879bc..cb7aac1 100644
--- a/src/com/android/quicksearchbox/SuggestionCursor.java
+++ b/src/com/android/quicksearchbox/SuggestionCursor.java
@@ -173,4 +173,9 @@
* Checks if this suggestion is a shortcut.
*/
boolean isSuggestionShortcut();
+
+ /**
+ * Checks if this is a web search suggestion.
+ */
+ boolean isWebSearchSuggestion();
}
diff --git a/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java b/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
index 516e77c..8c45251 100644
--- a/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
+++ b/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
@@ -106,7 +106,7 @@
protected void updateRefinable(SuggestionCursor suggestion) {
boolean refinable =
- Intent.ACTION_WEB_SEARCH.equals(suggestion.getSuggestionIntentAction())
+ suggestion.isWebSearchSuggestion()
&& mIcon2.getDrawable() == null
&& !TextUtils.isEmpty(suggestion.getSuggestionQuery());
setRefinable(suggestion, refinable);
diff --git a/src/com/android/quicksearchbox/ui/QueryTextView.java b/src/com/android/quicksearchbox/ui/QueryTextView.java
new file mode 100644
index 0000000..5b56aa1
--- /dev/null
+++ b/src/com/android/quicksearchbox/ui/QueryTextView.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+/**
+ * The query text field.
+ */
+public class QueryTextView extends EditText {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "QSB.QueryTextView";
+
+ private SuggestionClickListener mSuggestionClickListener;
+
+ public QueryTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public QueryTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QueryTextView(Context context) {
+ super(context);
+ }
+
+ /**
+ * Sets the text selection in the query text view.
+ *
+ * @param selectAll If {@code true}, selects the entire query.
+ * If {@false}, no characters are selected, and the cursor is placed
+ * at the end of the query.
+ */
+ public void setTextSelection(boolean selectAll) {
+ if (selectAll) {
+ selectAll();
+ } else {
+ setSelection(length());
+ }
+ }
+
+ protected void replaceText(CharSequence text) {
+ clearComposingText();
+ setText(text);
+ setTextSelection(false);
+ }
+
+ public void setSuggestionClickListener(SuggestionClickListener listener) {
+ mSuggestionClickListener = listener;
+ }
+
+ private InputMethodManager getInputMethodManager() {
+ return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+
+ public void showInputMethod() {
+ InputMethodManager imm = getInputMethodManager();
+ if (imm != null) {
+ imm.showSoftInput(this, 0);
+ }
+ }
+
+ public void hideInputMethod() {
+ InputMethodManager imm = getInputMethodManager();
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+
+ @Override
+ public void onCommitCompletion(CompletionInfo completion) {
+ if (DBG) Log.d(TAG, "onCommitCompletion(" + completion + ")");
+ hideInputMethod();
+ replaceText(completion.getText());
+ if (mSuggestionClickListener != null) {
+ mSuggestionClickListener.onSuggestionClicked(completion.getPosition());
+ }
+ }
+
+}