SDK only: improve SearchViewCompat

There is an issue with SearchView where if you use back to dismiss
it, the search text does not get cleared.  This change fixes it in
the support library by overriding the callback about the action
view being collapsed to also clear the search text.  (Note that this
will only be fixed in API 14 or later since this callback doesn't
exist on earlier versions.)

Also add a bunch of additional methods for performing API calls
on the SearchView.

Change-Id: I97241f165eb6076329ff7108c4596da72d02cb6c
diff --git a/v4/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java b/v4/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
index c03556f..eeadfab 100644
--- a/v4/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
+++ b/v4/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
@@ -16,9 +16,12 @@
 
 package android.support.v4.widget;
 
+import android.app.SearchManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.view.View;
 import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
 
 /**
@@ -31,10 +34,21 @@
         public boolean onQueryTextChange(String newText);
     }
 
+    interface OnCloseListenerCompatBridge {
+        public boolean onClose();
+    }
+
     public static View newSearchView(Context context) {
         return new SearchView(context);
     }
 
+    public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
+        SearchView sv = ((SearchView) searchView);
+        SearchManager searchManager = (SearchManager)
+                sv.getContext().getSystemService(Context.SEARCH_SERVICE);
+        sv.setSearchableInfo(searchManager.getSearchableInfo(searchableComponent));
+    }
+
     public static Object newOnQueryTextListener(final OnQueryTextListenerCompatBridge listener) {
         return new OnQueryTextListener() {
             @Override
@@ -52,4 +66,57 @@
     public static void setOnQueryTextListener(Object searchView, Object listener) {
         ((SearchView) searchView).setOnQueryTextListener((OnQueryTextListener) listener);
     }
+
+    public static Object newOnCloseListener(final OnCloseListenerCompatBridge listener) {
+        return new OnCloseListener() {
+            @Override
+            public boolean onClose() {
+                return listener.onClose();
+            }
+        };
+    }
+
+    public static void setOnCloseListener(Object searchView, Object listener) {
+        ((SearchView) searchView).setOnCloseListener((OnCloseListener) listener);
+    }
+
+    public static CharSequence getQuery(View searchView) {
+        return ((SearchView) searchView).getQuery();
+    }
+
+    public static void setQuery(View searchView, CharSequence query, boolean submit) {
+        ((SearchView) searchView).setQuery(query, submit);
+    }
+
+    public static void setQueryHint(View searchView, CharSequence hint) {
+        ((SearchView) searchView).setQueryHint(hint);
+    }
+
+    public static void setIconified(View searchView, boolean iconify) {
+        ((SearchView) searchView).setIconified(iconify);
+    }
+
+    public static boolean isIconified(View searchView) {
+        return ((SearchView) searchView).isIconified();
+    }
+
+    public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
+        ((SearchView) searchView).setSubmitButtonEnabled(enabled);
+    }
+
+    public static boolean isSubmitButtonEnabled(View searchView) {
+        return ((SearchView) searchView).isSubmitButtonEnabled();
+    }
+
+    public static void setQueryRefinementEnabled(View searchView, boolean enable) {
+        ((SearchView) searchView).setQueryRefinementEnabled(enable);
+    }
+
+    public static boolean isQueryRefinementEnabled(View searchView) {
+        return ((SearchView) searchView).isQueryRefinementEnabled();
+    }
+
+    public static void setMaxWidth(View searchView, int maxpixels) {
+        ((SearchView) searchView).setMaxWidth(maxpixels);
+    }
 }
diff --git a/v4/ics/android/support/v4/widget/SearchViewCompatIcs.java b/v4/ics/android/support/v4/widget/SearchViewCompatIcs.java
new file mode 100644
index 0000000..b8d719c
--- /dev/null
+++ b/v4/ics/android/support/v4/widget/SearchViewCompatIcs.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 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 android.support.v4.widget;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.SearchView;
+
+/**
+ * Implementation of SearchView compatibility that can call ICS APIs.
+ */
+class SearchViewCompatIcs {
+
+    public static class MySearchView extends SearchView {
+        public MySearchView(Context context) {
+            super(context);
+        }
+
+        // The normal SearchView doesn't clear its search text when
+        // collapsed, so we will do this for it.
+        @Override
+        public void onActionViewCollapsed() {
+            setQuery("", false);
+            super.onActionViewCollapsed();
+        }
+    }
+
+    public static View newSearchView(Context context) {
+        return new MySearchView(context);
+    }
+
+    public static void setImeOptions(View searchView, int imeOptions) {
+        ((SearchView) searchView).setImeOptions(imeOptions);
+    }
+
+    public static void setInputType(View searchView, int inputType) {
+        ((SearchView) searchView).setInputType(inputType);
+    }
+}
diff --git a/v4/java/android/support/v4/widget/SearchViewCompat.java b/v4/java/android/support/v4/widget/SearchViewCompat.java
index 2956839..b52c7f6 100644
--- a/v4/java/android/support/v4/widget/SearchViewCompat.java
+++ b/v4/java/android/support/v4/widget/SearchViewCompat.java
@@ -16,9 +16,12 @@
 
 package android.support.v4.widget;
 
+import android.app.SearchManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.Build;
 import android.view.View;
+import android.widget.TextView;
 
 /**
  * Helper for accessing features in {@link android.widget.SearchView}
@@ -28,8 +31,23 @@
 
     interface SearchViewCompatImpl {
         View newSearchView(Context context);
+        void setSearchableInfo(View searchView, ComponentName searchableComponent);
+        void setImeOptions(View searchView, int imeOptions);
+        void setInputType(View searchView, int inputType);
         Object newOnQueryTextListener(OnQueryTextListenerCompat listener);
         void setOnQueryTextListener(Object searchView, Object listener);
+        Object newOnCloseListener(OnCloseListenerCompat listener);
+        void setOnCloseListener(Object searchView, Object listener);
+        CharSequence getQuery(View searchView);
+        void setQuery(View searchView, CharSequence query, boolean submit);
+        void setQueryHint(View searchView, CharSequence hint);
+        void setIconified(View searchView, boolean iconify);
+        boolean isIconified(View searchView);
+        void setSubmitButtonEnabled(View searchView, boolean enabled);
+        boolean isSubmitButtonEnabled(View searchView);
+        void setQueryRefinementEnabled(View searchView, boolean enable);
+        boolean isQueryRefinementEnabled(View searchView);
+        void setMaxWidth(View searchView, int maxpixels);
     }
 
     static class SearchViewCompatStubImpl implements SearchViewCompatImpl {
@@ -40,13 +58,77 @@
         }
 
         @Override
+        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
+        }
+
+        @Override
+        public void setImeOptions(View searchView, int imeOptions) {
+        }
+
+        @Override
+        public void setInputType(View searchView, int inputType) {
+        }
+
+        @Override
         public Object newOnQueryTextListener(OnQueryTextListenerCompat listener) {
             return null;
         }
 
         @Override
         public void setOnQueryTextListener(Object searchView, Object listener) {
+        }
 
+        @Override
+        public Object newOnCloseListener(OnCloseListenerCompat listener) {
+            return null;
+        }
+
+        @Override
+        public void setOnCloseListener(Object searchView, Object listener) {
+        }
+
+        @Override
+        public CharSequence getQuery(View searchView) {
+            return null;
+        }
+
+        @Override
+        public void setQuery(View searchView, CharSequence query, boolean submit) {
+        }
+
+        @Override
+        public void setQueryHint(View searchView, CharSequence hint) {
+        }
+
+        @Override
+        public void setIconified(View searchView, boolean iconify) {
+        }
+
+        @Override
+        public boolean isIconified(View searchView) {
+            return true;
+        }
+
+        @Override
+        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
+        }
+
+        @Override
+        public boolean isSubmitButtonEnabled(View searchView) {
+            return false;
+        }
+
+        @Override
+        public void setQueryRefinementEnabled(View searchView, boolean enable) {
+        }
+
+        @Override
+        public boolean isQueryRefinementEnabled(View searchView) {
+            return false;
+        }
+
+        @Override
+        public void setMaxWidth(View searchView, int maxpixels) {
         }
     }
 
@@ -58,6 +140,11 @@
         }
 
         @Override
+        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
+            SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
+        }
+
+        @Override
         public Object newOnQueryTextListener(final OnQueryTextListenerCompat listener) {
             return SearchViewCompatHoneycomb.newOnQueryTextListener(
                     new SearchViewCompatHoneycomb.OnQueryTextListenerCompatBridge() {
@@ -76,12 +163,98 @@
         public void setOnQueryTextListener(Object searchView, Object listener) {
             SearchViewCompatHoneycomb.setOnQueryTextListener(searchView, listener);
         }
+
+        @Override
+        public Object newOnCloseListener(final OnCloseListenerCompat listener) {
+            return SearchViewCompatHoneycomb.newOnCloseListener(
+                    new SearchViewCompatHoneycomb.OnCloseListenerCompatBridge() {
+                        @Override
+                        public boolean onClose() {
+                            return listener.onClose();
+                        }
+                    });
+        }
+
+        @Override
+        public void setOnCloseListener(Object searchView, Object listener) {
+            SearchViewCompatHoneycomb.setOnCloseListener(searchView, listener);
+        }
+
+        @Override
+        public CharSequence getQuery(View searchView) {
+            return SearchViewCompatHoneycomb.getQuery(searchView);
+        }
+
+        @Override
+        public void setQuery(View searchView, CharSequence query, boolean submit) {
+            SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
+        }
+
+        @Override
+        public void setQueryHint(View searchView, CharSequence hint) {
+            SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
+        }
+
+        @Override
+        public void setIconified(View searchView, boolean iconify) {
+            SearchViewCompatHoneycomb.setIconified(searchView, iconify);
+        }
+
+        @Override
+        public boolean isIconified(View searchView) {
+            return SearchViewCompatHoneycomb.isIconified(searchView);
+        }
+
+        @Override
+        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
+            SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
+        }
+
+        @Override
+        public boolean isSubmitButtonEnabled(View searchView) {
+            return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
+        }
+
+        @Override
+        public void setQueryRefinementEnabled(View searchView, boolean enable) {
+            SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
+        }
+
+        @Override
+        public boolean isQueryRefinementEnabled(View searchView) {
+            return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
+        }
+
+        @Override
+        public void setMaxWidth(View searchView, int maxpixels) {
+            SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
+        }
+    }
+
+    static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {
+
+        @Override
+        public View newSearchView(Context context) {
+            return SearchViewCompatIcs.newSearchView(context);
+        }
+
+        @Override
+        public void setImeOptions(View searchView, int imeOptions) {
+            SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
+        }
+
+        @Override
+        public void setInputType(View searchView, int inputType) {
+            SearchViewCompatIcs.setInputType(searchView, inputType);
+        }
     }
 
     private static final SearchViewCompatImpl IMPL;
 
     static {
-        if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
+        if (Build.VERSION.SDK_INT >= 14) { // ICS
+            IMPL = new SearchViewCompatIcsImpl();
+        } else if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
             IMPL = new SearchViewCompatHoneycombImpl();
         } else {
             IMPL = new SearchViewCompatStubImpl();
@@ -104,6 +277,46 @@
     }
 
     /**
+     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
+     * to display labels, hints, suggestions, create intents for launching search results screens
+     * and controlling other affordances such as a voice button.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param searchableComponent.  The application component whose
+     * {@link android.app.SearchableInfo} should be loaded and applied to
+     * the SearchView.
+     */
+    public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
+        IMPL.setSearchableInfo(searchView, searchableComponent);
+    }
+
+    /**
+     * Sets the IME options on the query text field.  This is a no-op if
+     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
+     * platforms.
+     *
+     * @see TextView#setImeOptions(int)
+     * @param searchView The SearchView to operate on.
+     * @param imeOptions the options to set on the query text field
+     */
+    public static void setImeOptions(View searchView, int imeOptions) {
+        IMPL.setImeOptions(searchView, imeOptions);
+    }
+
+    /**
+     * Sets the input type on the query text field.  This is a no-op if
+     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
+     * platforms.
+     *
+     * @see TextView#setInputType(int)
+     * @param searchView The SearchView to operate on.
+     * @param inputType the input type to set on the query text field
+     */
+    public static void setInputType(View searchView, int inputType) {
+        IMPL.setInputType(searchView, inputType);
+    }
+
+    /**
      * Sets a listener for user actions within the SearchView.
      *
      * @param searchView The SearchView in which to register the listener.
@@ -152,4 +365,155 @@
             return false;
         }
     }
+
+    /**
+     * Sets a listener to inform when the user closes the SearchView.
+     *
+     * @param searchView The SearchView in which to register the listener.
+     * @param listener the listener to call when the user closes the SearchView.
+     */
+    public static void setOnCloseListener(View searchView, OnCloseListenerCompat listener) {
+        IMPL.setOnCloseListener(searchView, listener.mListener);
+    }
+
+    /**
+     * Callback for closing the query UI.
+     */
+    public static abstract class OnCloseListenerCompat {
+        final Object mListener;
+
+        public OnCloseListenerCompat() {
+            mListener = IMPL.newOnCloseListener(this);
+        }
+
+        /**
+         * The user is attempting to close the SearchView.
+         *
+         * @return true if the listener wants to override the default behavior of clearing the
+         * text field and dismissing it, false otherwise.
+         */
+        public boolean onClose() {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the query string currently in the text field.
+     *
+     * @param searchView The SearchView to operate on.
+     *
+     * @return the query string
+     */
+    public static CharSequence getQuery(View searchView) {
+        return IMPL.getQuery(searchView);
+    }
+
+    /**
+     * Sets a query string in the text field and optionally submits the query as well.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param query the query string. This replaces any query text already present in the
+     * text field.
+     * @param submit whether to submit the query right now or only update the contents of
+     * text field.
+     */
+    public static void setQuery(View searchView, CharSequence query, boolean submit) {
+        IMPL.setQuery(searchView, query, submit);
+    }
+
+    /**
+     * Sets the hint text to display in the query text field. This overrides any hint specified
+     * in the SearchableInfo.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param hint the hint text to display
+     */
+    public static void setQueryHint(View searchView, CharSequence hint) {
+        IMPL.setQueryHint(searchView, hint);
+    }
+
+    /**
+     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
+     * a temporary state and does not override the default iconified state set by
+     * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
+     * a false here will only be valid until the user closes the field. And if the default
+     * state is expanded, then a true here will only clear the text field and not close it.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param iconify a true value will collapse the SearchView to an icon, while a false will
+     * expand it.
+     */
+    public static void setIconified(View searchView, boolean iconify) {
+        IMPL.setIconified(searchView, iconify);
+    }
+
+    /**
+     * Returns the current iconified state of the SearchView.
+     *
+     * @param searchView The SearchView to operate on.
+     * @return true if the SearchView is currently iconified, false if the search field is
+     * fully visible.
+     */
+    public static boolean isIconified(View searchView) {
+        return IMPL.isIconified(searchView);
+    }
+
+    /**
+     * Enables showing a submit button when the query is non-empty. In cases where the SearchView
+     * is being used to filter the contents of the current activity and doesn't launch a separate
+     * results activity, then the submit button should be disabled.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param enabled true to show a submit button for submitting queries, false if a submit
+     * button is not required.
+     */
+    public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
+        IMPL.setSubmitButtonEnabled(searchView, enabled);
+    }
+
+    /**
+     * Returns whether the submit button is enabled when necessary or never displayed.
+     *
+     * @param searchView The SearchView to operate on.
+     * @return whether the submit button is enabled automatically when necessary
+     */
+    public static boolean isSubmitButtonEnabled(View searchView) {
+        return IMPL.isSubmitButtonEnabled(searchView);
+    }
+
+    /**
+     * Specifies if a query refinement button should be displayed alongside each suggestion
+     * or if it should depend on the flags set in the individual items retrieved from the
+     * suggestions provider. Clicking on the query refinement button will replace the text
+     * in the query text field with the text from the suggestion. This flag only takes effect
+     * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
+     * and not when using a custom adapter.
+     *
+     * @param searchView The SearchView to operate on.
+     * @param enable true if all items should have a query refinement button, false if only
+     * those items that have a query refinement flag set should have the button.
+     *
+     * @see SearchManager#SUGGEST_COLUMN_FLAGS
+     * @see SearchManager#FLAG_QUERY_REFINEMENT
+     */
+    public static void setQueryRefinementEnabled(View searchView, boolean enable) {
+        IMPL.setQueryRefinementEnabled(searchView, enable);
+    }
+
+    /**
+     * Returns whether query refinement is enabled for all items or only specific ones.
+     * @param searchView The SearchView to operate on.
+     * @return true if enabled for all items, false otherwise.
+     */
+    public static boolean isQueryRefinementEnabled(View searchView) {
+        return IMPL.isQueryRefinementEnabled(searchView);
+    }
+
+    /**
+     * Makes the view at most this many pixels wide
+     * @param searchView The SearchView to operate on.
+     */
+    public static void setMaxWidth(View searchView, int maxpixels) {
+        IMPL.setMaxWidth(searchView, maxpixels);
+    }
 }