1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.support.v4.widget;
18 
19 import android.app.SearchManager;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.os.Build;
23 import android.view.View;
24 import android.widget.TextView;
25 
26 /**
27  * Helper for accessing features in {@link android.widget.SearchView}
28  * introduced after API level 4 in a backwards compatible fashion.
29  */
30 public class SearchViewCompat {
31 
32     interface SearchViewCompatImpl {
newSearchView(Context context)33         View newSearchView(Context context);
setSearchableInfo(View searchView, ComponentName searchableComponent)34         void setSearchableInfo(View searchView, ComponentName searchableComponent);
setImeOptions(View searchView, int imeOptions)35         void setImeOptions(View searchView, int imeOptions);
setInputType(View searchView, int inputType)36         void setInputType(View searchView, int inputType);
newOnQueryTextListener(OnQueryTextListenerCompat listener)37         Object newOnQueryTextListener(OnQueryTextListenerCompat listener);
setOnQueryTextListener(Object searchView, Object listener)38         void setOnQueryTextListener(Object searchView, Object listener);
newOnCloseListener(OnCloseListenerCompat listener)39         Object newOnCloseListener(OnCloseListenerCompat listener);
setOnCloseListener(Object searchView, Object listener)40         void setOnCloseListener(Object searchView, Object listener);
getQuery(View searchView)41         CharSequence getQuery(View searchView);
setQuery(View searchView, CharSequence query, boolean submit)42         void setQuery(View searchView, CharSequence query, boolean submit);
setQueryHint(View searchView, CharSequence hint)43         void setQueryHint(View searchView, CharSequence hint);
setIconified(View searchView, boolean iconify)44         void setIconified(View searchView, boolean iconify);
isIconified(View searchView)45         boolean isIconified(View searchView);
setSubmitButtonEnabled(View searchView, boolean enabled)46         void setSubmitButtonEnabled(View searchView, boolean enabled);
isSubmitButtonEnabled(View searchView)47         boolean isSubmitButtonEnabled(View searchView);
setQueryRefinementEnabled(View searchView, boolean enable)48         void setQueryRefinementEnabled(View searchView, boolean enable);
isQueryRefinementEnabled(View searchView)49         boolean isQueryRefinementEnabled(View searchView);
setMaxWidth(View searchView, int maxpixels)50         void setMaxWidth(View searchView, int maxpixels);
51     }
52 
53     static class SearchViewCompatStubImpl implements SearchViewCompatImpl {
54 
55         @Override
newSearchView(Context context)56         public View newSearchView(Context context) {
57             return null;
58         }
59 
60         @Override
setSearchableInfo(View searchView, ComponentName searchableComponent)61         public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
62         }
63 
64         @Override
setImeOptions(View searchView, int imeOptions)65         public void setImeOptions(View searchView, int imeOptions) {
66         }
67 
68         @Override
setInputType(View searchView, int inputType)69         public void setInputType(View searchView, int inputType) {
70         }
71 
72         @Override
newOnQueryTextListener(OnQueryTextListenerCompat listener)73         public Object newOnQueryTextListener(OnQueryTextListenerCompat listener) {
74             return null;
75         }
76 
77         @Override
setOnQueryTextListener(Object searchView, Object listener)78         public void setOnQueryTextListener(Object searchView, Object listener) {
79         }
80 
81         @Override
newOnCloseListener(OnCloseListenerCompat listener)82         public Object newOnCloseListener(OnCloseListenerCompat listener) {
83             return null;
84         }
85 
86         @Override
setOnCloseListener(Object searchView, Object listener)87         public void setOnCloseListener(Object searchView, Object listener) {
88         }
89 
90         @Override
getQuery(View searchView)91         public CharSequence getQuery(View searchView) {
92             return null;
93         }
94 
95         @Override
setQuery(View searchView, CharSequence query, boolean submit)96         public void setQuery(View searchView, CharSequence query, boolean submit) {
97         }
98 
99         @Override
setQueryHint(View searchView, CharSequence hint)100         public void setQueryHint(View searchView, CharSequence hint) {
101         }
102 
103         @Override
setIconified(View searchView, boolean iconify)104         public void setIconified(View searchView, boolean iconify) {
105         }
106 
107         @Override
isIconified(View searchView)108         public boolean isIconified(View searchView) {
109             return true;
110         }
111 
112         @Override
setSubmitButtonEnabled(View searchView, boolean enabled)113         public void setSubmitButtonEnabled(View searchView, boolean enabled) {
114         }
115 
116         @Override
isSubmitButtonEnabled(View searchView)117         public boolean isSubmitButtonEnabled(View searchView) {
118             return false;
119         }
120 
121         @Override
setQueryRefinementEnabled(View searchView, boolean enable)122         public void setQueryRefinementEnabled(View searchView, boolean enable) {
123         }
124 
125         @Override
isQueryRefinementEnabled(View searchView)126         public boolean isQueryRefinementEnabled(View searchView) {
127             return false;
128         }
129 
130         @Override
setMaxWidth(View searchView, int maxpixels)131         public void setMaxWidth(View searchView, int maxpixels) {
132         }
133     }
134 
135     static class SearchViewCompatHoneycombImpl extends SearchViewCompatStubImpl {
136 
137         @Override
newSearchView(Context context)138         public View newSearchView(Context context) {
139             return SearchViewCompatHoneycomb.newSearchView(context);
140         }
141 
142         @Override
setSearchableInfo(View searchView, ComponentName searchableComponent)143         public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
144             SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
145         }
146 
147         @Override
newOnQueryTextListener(final OnQueryTextListenerCompat listener)148         public Object newOnQueryTextListener(final OnQueryTextListenerCompat listener) {
149             return SearchViewCompatHoneycomb.newOnQueryTextListener(
150                     new SearchViewCompatHoneycomb.OnQueryTextListenerCompatBridge() {
151                         @Override
152                         public boolean onQueryTextSubmit(String query) {
153                             return listener.onQueryTextSubmit(query);
154                         }
155                         @Override
156                         public boolean onQueryTextChange(String newText) {
157                             return listener.onQueryTextChange(newText);
158                         }
159                     });
160         }
161 
162         @Override
setOnQueryTextListener(Object searchView, Object listener)163         public void setOnQueryTextListener(Object searchView, Object listener) {
164             SearchViewCompatHoneycomb.setOnQueryTextListener(searchView, listener);
165         }
166 
167         @Override
newOnCloseListener(final OnCloseListenerCompat listener)168         public Object newOnCloseListener(final OnCloseListenerCompat listener) {
169             return SearchViewCompatHoneycomb.newOnCloseListener(
170                     new SearchViewCompatHoneycomb.OnCloseListenerCompatBridge() {
171                         @Override
172                         public boolean onClose() {
173                             return listener.onClose();
174                         }
175                     });
176         }
177 
178         @Override
179         public void setOnCloseListener(Object searchView, Object listener) {
180             SearchViewCompatHoneycomb.setOnCloseListener(searchView, listener);
181         }
182 
183         @Override
184         public CharSequence getQuery(View searchView) {
185             return SearchViewCompatHoneycomb.getQuery(searchView);
186         }
187 
188         @Override
189         public void setQuery(View searchView, CharSequence query, boolean submit) {
190             SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
191         }
192 
193         @Override
194         public void setQueryHint(View searchView, CharSequence hint) {
195             SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
196         }
197 
198         @Override
199         public void setIconified(View searchView, boolean iconify) {
200             SearchViewCompatHoneycomb.setIconified(searchView, iconify);
201         }
202 
203         @Override
204         public boolean isIconified(View searchView) {
205             return SearchViewCompatHoneycomb.isIconified(searchView);
206         }
207 
208         @Override
209         public void setSubmitButtonEnabled(View searchView, boolean enabled) {
210             SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
211         }
212 
213         @Override
214         public boolean isSubmitButtonEnabled(View searchView) {
215             return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
216         }
217 
218         @Override
219         public void setQueryRefinementEnabled(View searchView, boolean enable) {
220             SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
221         }
222 
223         @Override
224         public boolean isQueryRefinementEnabled(View searchView) {
225             return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
226         }
227 
228         @Override
229         public void setMaxWidth(View searchView, int maxpixels) {
230             SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
231         }
232     }
233 
234     static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {
235 
236         @Override
237         public View newSearchView(Context context) {
238             return SearchViewCompatIcs.newSearchView(context);
239         }
240 
241         @Override
242         public void setImeOptions(View searchView, int imeOptions) {
243             SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
244         }
245 
246         @Override
247         public void setInputType(View searchView, int inputType) {
248             SearchViewCompatIcs.setInputType(searchView, inputType);
249         }
250     }
251 
252     private static final SearchViewCompatImpl IMPL;
253 
254     static {
255         if (Build.VERSION.SDK_INT >= 14) { // ICS
256             IMPL = new SearchViewCompatIcsImpl();
257         } else if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
258             IMPL = new SearchViewCompatHoneycombImpl();
259         } else {
260             IMPL = new SearchViewCompatStubImpl();
261         }
262     }
263 
264     private SearchViewCompat(Context context) {
265         /* Hide constructor */
266     }
267 
268     /**
269      * Creates a new SearchView.
270      *
271      * @param context The Context the view is running in.
272      * @return A SearchView instance if the class is present on the current
273      *         platform, null otherwise.
274      */
275     public static View newSearchView(Context context) {
276         return IMPL.newSearchView(context);
277     }
278 
279     /**
280      * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
281      * to display labels, hints, suggestions, create intents for launching search results screens
282      * and controlling other affordances such as a voice button.
283      *
284      * @param searchView The SearchView to operate on.
285      * @param searchableComponent The application component whose
286      * {@link android.app.SearchableInfo} should be loaded and applied to
287      * the SearchView.
288      */
289     public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
290         IMPL.setSearchableInfo(searchView, searchableComponent);
291     }
292 
293     /**
294      * Sets the IME options on the query text field.  This is a no-op if
295      * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
296      * platforms.
297      *
298      * @see TextView#setImeOptions(int)
299      * @param searchView The SearchView to operate on.
300      * @param imeOptions the options to set on the query text field
301      */
302     public static void setImeOptions(View searchView, int imeOptions) {
303         IMPL.setImeOptions(searchView, imeOptions);
304     }
305 
306     /**
307      * Sets the input type on the query text field.  This is a no-op if
308      * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
309      * platforms.
310      *
311      * @see TextView#setInputType(int)
312      * @param searchView The SearchView to operate on.
313      * @param inputType the input type to set on the query text field
314      */
315     public static void setInputType(View searchView, int inputType) {
316         IMPL.setInputType(searchView, inputType);
317     }
318 
319     /**
320      * Sets a listener for user actions within the SearchView.
321      *
322      * @param searchView The SearchView in which to register the listener.
323      * @param listener the listener object that receives callbacks when the user performs
324      *     actions in the SearchView such as clicking on buttons or typing a query.
325      */
326     public static void setOnQueryTextListener(View searchView, OnQueryTextListenerCompat listener) {
327         IMPL.setOnQueryTextListener(searchView, listener.mListener);
328     }
329 
330     /**
331      * Callbacks for changes to the query text.
332      */
333     public static abstract class OnQueryTextListenerCompat {
334         final Object mListener;
335 
336         public OnQueryTextListenerCompat() {
337             mListener = IMPL.newOnQueryTextListener(this);
338         }
339 
340         /**
341          * Called when the user submits the query. This could be due to a key press on the
342          * keyboard or due to pressing a submit button.
343          * The listener can override the standard behavior by returning true
344          * to indicate that it has handled the submit request. Otherwise return false to
345          * let the SearchView handle the submission by launching any associated intent.
346          *
347          * @param query the query text that is to be submitted
348          *
349          * @return true if the query has been handled by the listener, false to let the
350          * SearchView perform the default action.
351          */
352         public boolean onQueryTextSubmit(String query) {
353             return false;
354         }
355 
356         /**
357          * Called when the query text is changed by the user.
358          *
359          * @param newText the new content of the query text field.
360          *
361          * @return false if the SearchView should perform the default action of showing any
362          * suggestions if available, true if the action was handled by the listener.
363          */
364         public boolean onQueryTextChange(String newText) {
365             return false;
366         }
367     }
368 
369     /**
370      * Sets a listener to inform when the user closes the SearchView.
371      *
372      * @param searchView The SearchView in which to register the listener.
373      * @param listener the listener to call when the user closes the SearchView.
374      */
375     public static void setOnCloseListener(View searchView, OnCloseListenerCompat listener) {
376         IMPL.setOnCloseListener(searchView, listener.mListener);
377     }
378 
379     /**
380      * Callback for closing the query UI.
381      */
382     public static abstract class OnCloseListenerCompat {
383         final Object mListener;
384 
385         public OnCloseListenerCompat() {
386             mListener = IMPL.newOnCloseListener(this);
387         }
388 
389         /**
390          * The user is attempting to close the SearchView.
391          *
392          * @return true if the listener wants to override the default behavior of clearing the
393          * text field and dismissing it, false otherwise.
394          */
395         public boolean onClose() {
396             return false;
397         }
398     }
399 
400     /**
401      * Returns the query string currently in the text field.
402      *
403      * @param searchView The SearchView to operate on.
404      *
405      * @return the query string
406      */
407     public static CharSequence getQuery(View searchView) {
408         return IMPL.getQuery(searchView);
409     }
410 
411     /**
412      * Sets a query string in the text field and optionally submits the query as well.
413      *
414      * @param searchView The SearchView to operate on.
415      * @param query the query string. This replaces any query text already present in the
416      * text field.
417      * @param submit whether to submit the query right now or only update the contents of
418      * text field.
419      */
420     public static void setQuery(View searchView, CharSequence query, boolean submit) {
421         IMPL.setQuery(searchView, query, submit);
422     }
423 
424     /**
425      * Sets the hint text to display in the query text field. This overrides any hint specified
426      * in the SearchableInfo.
427      *
428      * @param searchView The SearchView to operate on.
429      * @param hint the hint text to display
430      */
431     public static void setQueryHint(View searchView, CharSequence hint) {
432         IMPL.setQueryHint(searchView, hint);
433     }
434 
435     /**
436      * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
437      * a temporary state and does not override the default iconified state set by
438      * setIconifiedByDefault(boolean). If the default state is iconified, then
439      * a false here will only be valid until the user closes the field. And if the default
440      * state is expanded, then a true here will only clear the text field and not close it.
441      *
442      * @param searchView The SearchView to operate on.
443      * @param iconify a true value will collapse the SearchView to an icon, while a false will
444      * expand it.
445      */
446     public static void setIconified(View searchView, boolean iconify) {
447         IMPL.setIconified(searchView, iconify);
448     }
449 
450     /**
451      * Returns the current iconified state of the SearchView.
452      *
453      * @param searchView The SearchView to operate on.
454      * @return true if the SearchView is currently iconified, false if the search field is
455      * fully visible.
456      */
457     public static boolean isIconified(View searchView) {
458         return IMPL.isIconified(searchView);
459     }
460 
461     /**
462      * Enables showing a submit button when the query is non-empty. In cases where the SearchView
463      * is being used to filter the contents of the current activity and doesn't launch a separate
464      * results activity, then the submit button should be disabled.
465      *
466      * @param searchView The SearchView to operate on.
467      * @param enabled true to show a submit button for submitting queries, false if a submit
468      * button is not required.
469      */
470     public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
471         IMPL.setSubmitButtonEnabled(searchView, enabled);
472     }
473 
474     /**
475      * Returns whether the submit button is enabled when necessary or never displayed.
476      *
477      * @param searchView The SearchView to operate on.
478      * @return whether the submit button is enabled automatically when necessary
479      */
480     public static boolean isSubmitButtonEnabled(View searchView) {
481         return IMPL.isSubmitButtonEnabled(searchView);
482     }
483 
484     /**
485      * Specifies if a query refinement button should be displayed alongside each suggestion
486      * or if it should depend on the flags set in the individual items retrieved from the
487      * suggestions provider. Clicking on the query refinement button will replace the text
488      * in the query text field with the text from the suggestion. This flag only takes effect
489      * if a SearchableInfo has been specified with {@link #setSearchableInfo(View, ComponentName)}
490      * and not when using a custom adapter.
491      *
492      * @param searchView The SearchView to operate on.
493      * @param enable true if all items should have a query refinement button, false if only
494      * those items that have a query refinement flag set should have the button.
495      *
496      * @see SearchManager#SUGGEST_COLUMN_FLAGS
497      * @see SearchManager#FLAG_QUERY_REFINEMENT
498      */
499     public static void setQueryRefinementEnabled(View searchView, boolean enable) {
500         IMPL.setQueryRefinementEnabled(searchView, enable);
501     }
502 
503     /**
504      * Returns whether query refinement is enabled for all items or only specific ones.
505      * @param searchView The SearchView to operate on.
506      * @return true if enabled for all items, false otherwise.
507      */
508     public static boolean isQueryRefinementEnabled(View searchView) {
509         return IMPL.isQueryRefinementEnabled(searchView);
510     }
511 
512     /**
513      * Makes the view at most this many pixels wide
514      * @param searchView The SearchView to operate on.
515      */
516     public static void setMaxWidth(View searchView, int maxpixels) {
517         IMPL.setMaxWidth(searchView, maxpixels);
518     }
519 }
520