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());
+        }
+    }
+
+}